#pragma warning(disable:4530)
#include <windows.h>
#include <tchar.h>
#include <sstream>
#include <algorithm>
#include <assert.h>
#include "SqlData.h"

//! To enable SQLite's meta data methods define SQLITE3EX3_HAVE_METADATA as 1 here.
//! Attention: SQLite needs to be compiled with SQLITE_ENABLE_COLUMN_METADATA for this to work
#ifndef SQLITE3EX3_HAVE_METADATA
#define SQLITE3EX3_HAVE_METADATA 0
#endif

//! To enable SQLite's loadable extension feature define SQLITE3EX3_HAVE_LOAD_EXTENSION as 1 here.
//! Attention: SQLite needs to be compiled without SQLITE_OMIT_LOAD_EXTENSION for this to work
#ifndef SQLITE3EX3_HAVE_LOAD_EXTENSION
#define SQLITE3EX3_HAVE_LOAD_EXTENSION 0
#endif

//! To disable support for named collections define SQLITE3EX3_USE_NAMED_COLLECTIONS as 0 here.
//! Attention: if SQLITE3EX3_USE_NAMED_COLLECTIONS is defined as 1 (default) SQLite needs to be
//! compiled without SQLITE_OMIT_VIRTUALTABLE for this to work
#ifndef SQLITE3EX3_USE_NAMED_COLLECTIONS
#define SQLITE3EX3_USE_NAMED_COLLECTIONS 1
#endif

// Suppress some Visual C++ warnings regarding the default constructor
// for a C struct used only in SQLite modules
#ifdef __VISUALC__
#pragma warning (disable:4510 4610)
#endif

#ifndef SQLITE_HAS_CODEC
#define SQLITE_HAS_CODEC
#endif
#include "../sqlite3/sqlite3.h"

using namespace std;

// Error messages
const CharEx* ERRMSG_NODB          = _T("No Database opened");
const CharEx* ERRMSG_NOSTMT        = _T("Statement not accessible");
const CharEx* ERRMSG_NOMEM         = _T("Out of memory");
const CharEx* ERRMSG_DECODE        = _T("Cannot decode binary");
const CharEx* ERRMSG_INVALID_INDEX = _T("Invalid field index");
const CharEx* ERRMSG_INVALID_NAME  = _T("Invalid field name");
const CharEx* ERRMSG_INVALID_ROW   = _T("Invalid row index");
const CharEx* ERRMSG_INVALID_QUERY = _T("Invalid scalar query");
const CharEx* ERRMSG_INVALID_BLOB  = _T("Invalid BLOB handle");

const CharEx* ERRMSG_NORESULT      = _T("Null Results pointer");
const CharEx* ERRMSG_BIND_STR      = _T("Error binding string param");
const CharEx* ERRMSG_BIND_INT      = _T("Error binding int param");
const CharEx* ERRMSG_BIND_INT64    = _T("Error binding int64 param");
const CharEx* ERRMSG_BIND_DBL      = _T("Error binding double param");
const CharEx* ERRMSG_BIND_BLOB     = _T("Error binding blob param");
const CharEx* ERRMSG_BIND_DATETIME = _T("Error binding date/time param");
const CharEx* ERRMSG_BIND_NULL     = _T("Error binding NULL param");
const CharEx* ERRMSG_BIND_ZEROBLOB = _T("Error binding zero blob param");
const CharEx* ERRMSG_BIND_CLEAR    = _T("Error clearing bindings");
const CharEx* ERRMSG_NOTOWNED      = _T("Transfer of statement ownership not possible");

const CharEx* ERRMSG_NOMETADATA    = _T("Meta data support not available");
const CharEx* ERRMSG_NOCODEC       = _T("Encryption support not available");
const CharEx* ERRMSG_NOLOADEXT     = _T("Loadable extension support not available");
const CharEx* ERRMSG_NOINCBLOB     = _T("Incremental BLOB support not available");
const CharEx* ERRMSG_NOSAVEPOINT   = _T("Savepoint support not available");
const CharEx* ERRMSG_NOBACKUP      = _T("Backup/restore support not available");
const CharEx* ERRMSG_NOWAL         = _T("Write Ahead Log support not available");
const CharEx* ERRMSG_NOCOLLECTIONS = _T("Named collection support not available");

const CharEx* ERRMSG_SHARED_CACHE  = _T("Setting SQLite shared cache mode failed");

const CharEx* ERRMSG_INITIALIZE    = _T("Initialization of SQLite failed");
const CharEx* ERRMSG_SHUTDOWN      = _T("Shutdown of SQLite failed");

const CharEx* ERRMSG_SOURCEDB_BUSY   = _T("Source database is busy");
const CharEx* ERRMSG_DBOPEN_FAILED   = _T("Database open failed");
const CharEx* ERRMSG_DBASSIGN_FAILED = _T("Database assignment failed");

// ----------------------------------------------------------------------------
// inline conversion from StringEx to Int64Ex
// ----------------------------------------------------------------------------

inline Int64Ex ConvertStringToLongLong(const StringEx& str, Int64Ex defValue /*=0*/)
{
  size_t n = str.length();
  size_t j = 0;
  Int64Ex value = 0;
  bool negative = false;

  if (str[j] == '-')
  {
    negative = true;
    j++;
  }

  while (j < n)
  {
    if (str[j] < '0' || str[j] > '9')
    {
      return defValue;
    }
    value *= 10;
    value += (str[j] - '0');
    j++;
  }

  return negative ? -value : value;
}

inline long ConvertStringToLong(const StringEx& str, long defValue /*=0*/)
{
  size_t n = str.length();
  size_t j = 0;
  long value = 0;
  bool negative = false;

  if (str[j] == '-')
  {
    negative = true;
    j++;
  }

  while (j < n)
  {
    if (str[j] < '0' || str[j] > '9')
    {
      return defValue;
    }
    value *= 10;
    value += (str[j] - '0');
    j++;
  }

  return negative ? -value : value;
}

string Toupper(const StringEx & s)
{
  string d (s);
  transform (d.begin (), d.end (), d.begin (), (int(*)(int)) toupper);
  return d;
}

// ----------------------------------------------------------------------------
// SQLite3Exception: class
// ----------------------------------------------------------------------------

SQLite3Exception::SQLite3Exception(int errorCode, const StringEx& errorMsg)
  : m_errorCode(errorCode)
{
  stringstream ss;
  ss << errorCode;
  m_errorMessage = ErrorCodeAsString(errorCode) + _T("[") +
                   ss.str() + _T("]: ") +errorMsg;

}

SQLite3Exception::SQLite3Exception(const SQLite3Exception&  e)
  : m_errorCode(e.m_errorCode), m_errorMessage(e.m_errorMessage)
{
}

const StringEx SQLite3Exception::ErrorCodeAsString(int errorCode)
{
  switch (errorCode)
  {
    case SQLITE_OK          : return _T("SQLITE_OK");
    case SQLITE_ERROR       : return _T("SQLITE_ERROR");
    case SQLITE_INTERNAL    : return _T("SQLITE_INTERNAL");
    case SQLITE_PERM        : return _T("SQLITE_PERM");
    case SQLITE_ABORT       : return _T("SQLITE_ABORT");
    case SQLITE_BUSY        : return _T("SQLITE_BUSY");
    case SQLITE_LOCKED      : return _T("SQLITE_LOCKED");
    case SQLITE_NOMEM       : return _T("SQLITE_NOMEM");
    case SQLITE_READONLY    : return _T("SQLITE_READONLY");
    case SQLITE_INTERRUPT   : return _T("SQLITE_INTERRUPT");
    case SQLITE_IOERR       : return _T("SQLITE_IOERR");
    case SQLITE_CORRUPT     : return _T("SQLITE_CORRUPT");
    case SQLITE_NOTFOUND    : return _T("SQLITE_NOTFOUND");
    case SQLITE_FULL        : return _T("SQLITE_FULL");
    case SQLITE_CANTOPEN    : return _T("SQLITE_CANTOPEN");
    case SQLITE_PROTOCOL    : return _T("SQLITE_PROTOCOL");
    case SQLITE_EMPTY       : return _T("SQLITE_EMPTY");
    case SQLITE_SCHEMA      : return _T("SQLITE_SCHEMA");
    case SQLITE_TOOBIG      : return _T("SQLITE_TOOBIG");
    case SQLITE_CONSTRAINT  : return _T("SQLITE_CONSTRAINT");
    case SQLITE_MISMATCH    : return _T("SQLITE_MISMATCH");
    case SQLITE_MISUSE      : return _T("SQLITE_MISUSE");
    case SQLITE_NOLFS       : return _T("SQLITE_NOLFS");
    case SQLITE_AUTH        : return _T("SQLITE_AUTH");
    case SQLITE_FORMAT      : return _T("SQLITE_FORMAT");
    case SQLITE_RANGE       : return _T("SQLITE_RANGE");
    case SQLITE_NOTADB      : return _T("SQLITE_NOTADB");
    case SQLITE_ROW         : return _T("SQLITE_ROW");
    case SQLITE_DONE        : return _T("SQLITE_DONE");
    // Extended error codes
    case SQLITE_IOERR_READ       : return _T("SQLITE_IOERR_READ");
    case SQLITE_IOERR_SHORT_READ : return _T("SQLITE_IOERR_SHORT_READ");
    case SQLITE_IOERR_WRITE      : return _T("SQLITE_IOERR_WRITE");
    case SQLITE_IOERR_FSYNC      : return _T("SQLITE_IOERR_FSYNC");
    case SQLITE_IOERR_DIR_FSYNC  : return _T("SQLITE_IOERR_DIR_FSYNC");
    case SQLITE_IOERR_TRUNCATE   : return _T("SQLITE_IOERR_TRUNCATE");
    case SQLITE_IOERR_FSTAT      : return _T("SQLITE_IOERR_FSTAT");
    case SQLITE_IOERR_UNLOCK     : return _T("SQLITE_IOERR_UNLOCK");
    case SQLITE_IOERR_RDLOCK     : return _T("SQLITE_IOERR_RDLOCK");
    case SQLITE_IOERR_DELETE     : return _T("SQLITE_IOERR_DELETE");
#if SQLITE_VERSION_NUMBER >= 3004000
    case SQLITE_IOERR_BLOCKED    : return _T("SQLITE_IOERR_BLOCKED");
#endif
#if SQLITE_VERSION_NUMBER >= 3005001
    case SQLITE_IOERR_NOMEM      : return _T("SQLITE_IOERR_NOMEM");
#endif
#if SQLITE_VERSION_NUMBER >= 3006000
    case SQLITE_IOERR_ACCESS     : return _T("SQLITE_IOERR_ACCESS");
    case SQLITE_IOERR_CHECKRESERVEDLOCK : return _T("SQLITE_IOERR_CHECKRESERVEDLOCK");
#endif
#if SQLITE_VERSION_NUMBER >= 3006002
    case SQLITE_IOERR_LOCK       : return _T("SQLITE_IOERR_LOCK");
#endif
#if SQLITE_VERSION_NUMBER >= 3006007
    case SQLITE_IOERR_CLOSE      : return _T("SQLITE_IOERR_CLOSE");
    case SQLITE_IOERR_DIR_CLOSE  : return _T("SQLITE_IOERR_DIR_CLOSE");
#endif
#if SQLITE_VERSION_NUMBER >= 3007000
    case SQLITE_IOERR_SHMOPEN      : return _T("SQLITE_IOERR_SHMOPEN");
    case SQLITE_IOERR_SHMSIZE      : return _T("SQLITE_IOERR_SHMSIZE");
    case SQLITE_IOERR_SHMLOCK      : return _T("SQLITE_IOERR_SHMLOCK");
    case SQLITE_LOCKED_SHAREDCACHE : return _T("SQLITE_LOCKED_SHAREDCACHE");
    case SQLITE_BUSY_RECOVERY      : return _T("SQLITE_BUSY_RECOVERY");
    case SQLITE_CANTOPEN_NOTEMPDIR : return _T("SQLITE_CANTOPEN_NOTEMPDIR");
#endif

    case SQLITE3EX_ERROR     : return _T("SQLITE3EX_ERROR");
    default                 : return _T("UNKNOWN_ERROR");
  }
}

SQLite3Exception::~SQLite3Exception()
{
}

// ----------------------------------------------------------------------------
// SQLite3StatementBuffer: class providing a statement buffer
//                           for use with the SQLite3 vmprintf function
// ----------------------------------------------------------------------------

SQLite3StatementBuffer::SQLite3StatementBuffer()
{
  m_buffer = 0;
}

SQLite3StatementBuffer::~SQLite3StatementBuffer()
{
  Clear();
}

void SQLite3StatementBuffer::Clear()
{
  if (m_buffer)
  {
    sqlite3_free(m_buffer);
    m_buffer = 0;
  }

}

const char* SQLite3StatementBuffer::Format(const char* format, ...)
{
  Clear();
  va_list va;
  va_start(va, format);
  m_buffer = sqlite3_vmprintf(format, va);
  va_end(va);
  return m_buffer;
}

const char* SQLite3StatementBuffer::FormatV(const char* format, va_list va)
{
  Clear();
  m_buffer = sqlite3_vmprintf(format, va);
  return m_buffer;
}

// ----------------------------------------------------------------------------
// SQLite3ResultSet: class providing access to the result set of a query
// ----------------------------------------------------------------------------

SQLite3ResultSet::SQLite3ResultSet()
{
  m_db = 0;
  m_stmt = 0;
  m_eof = true;
  m_first = true;
  m_cols = 0;
  m_ownStmt = false;
}

SQLite3ResultSet::SQLite3ResultSet(const SQLite3ResultSet& resultSet)
{
  m_db = resultSet.m_db;
  m_stmt = resultSet.m_stmt;
  // Only one object can own the statement
  const_cast<SQLite3ResultSet&>(resultSet).m_stmt = 0;
  m_eof = resultSet.m_eof;
  m_first = resultSet.m_first;
  m_cols = resultSet.m_cols;
  m_ownStmt = resultSet.m_ownStmt;
}

SQLite3ResultSet::SQLite3ResultSet(void* db,
                                       void* stmt,
                                       bool eof,
                                       bool first,
                                       bool ownStmt /*=true*/)
{
  m_db = db;
  m_stmt = stmt;
  m_eof = eof;
  m_first = first;
  m_cols = sqlite3_column_count((sqlite3_stmt*) m_stmt);
  m_ownStmt = ownStmt;
}

SQLite3ResultSet::~SQLite3ResultSet()
{
  try
  {
    Finalize();
  }
  catch (...)
  {
  }
}

SQLite3ResultSet& SQLite3ResultSet::operator=(const SQLite3ResultSet& resultSet)
{
  if (this != &resultSet)
  {
    try
    {
      Finalize();
    }
    catch (...)
    {
    }
    m_db = resultSet.m_db;
    m_stmt = resultSet.m_stmt;
    // Only one object can own the statement
    const_cast<SQLite3ResultSet&>(resultSet).m_stmt = 0;
    m_eof = resultSet.m_eof;
    m_first = resultSet.m_first;
    m_cols = resultSet.m_cols;
    m_ownStmt = resultSet.m_ownStmt;
  }
  return *this;
}

int SQLite3ResultSet::GetColumnCount()
{
  CheckStmt();
  return m_cols;
}

StringEx SQLite3ResultSet::GetAsString(int columnIndex)
{
  CheckStmt();

  if (columnIndex < 0 || columnIndex > m_cols-1)
  {
    throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_INVALID_INDEX);
  }

  const char* localValue = (const char*) sqlite3_column_text((sqlite3_stmt*) m_stmt, columnIndex);
  return localValue;
}

StringEx SQLite3ResultSet::GetAsString(const StringEx& columnName)
{
  int columnIndex = FindColumnIndex(columnName);
  const char* localValue = (const char*) sqlite3_column_text((sqlite3_stmt*) m_stmt, columnIndex);
  return localValue;
}

int SQLite3ResultSet::GetInt(int columnIndex, int nullValue /* = 0 */)
{
  if (GetColumnType(columnIndex) == SQLITE_NULL)
  {
    return nullValue;
  }
  else
  {
    return sqlite3_column_int((sqlite3_stmt*) m_stmt, columnIndex);
  }
}


int SQLite3ResultSet::GetInt(const StringEx& columnName, int nullValue /* = 0 */)
{
  int columnIndex = FindColumnIndex(columnName);
  return GetInt(columnIndex, nullValue);
}

Int64Ex SQLite3ResultSet::GetInt64(int columnIndex, Int64Ex nullValue /* = 0 */)
{
  if (GetColumnType(columnIndex) == SQLITE_NULL)
  {
    return nullValue;
  }
  else
  {
    return Int64Ex(sqlite3_column_int64((sqlite3_stmt*) m_stmt, columnIndex));
  }
}

Int64Ex SQLite3ResultSet::GetInt64(const StringEx& columnName, Int64Ex nullValue /* = 0 */)
{
  int columnIndex = FindColumnIndex(columnName);
  return GetInt64(columnIndex, nullValue);
}

double SQLite3ResultSet::GetDouble(int columnIndex, double nullValue /* = 0.0 */)
{
  if (GetColumnType(columnIndex) == SQLITE_NULL)
  {
    return nullValue;
  }
  else
  {
    return sqlite3_column_double((sqlite3_stmt*) m_stmt, columnIndex);
  }
}

double SQLite3ResultSet::GetDouble(const StringEx& columnName, double nullValue /* = 0.0 */)
{
  int columnIndex = FindColumnIndex(columnName);
  return GetDouble(columnIndex, nullValue);
}

StringEx SQLite3ResultSet::GetString(int columnIndex, const StringEx& nullValue /* = "" */)
{
  if (GetColumnType(columnIndex) == SQLITE_NULL)
  {
    return nullValue;
  }
  else
  {
    const char* localValue = (const char*) sqlite3_column_text((sqlite3_stmt*) m_stmt, columnIndex);
    return localValue;
  }
}

StringEx SQLite3ResultSet::GetString(const StringEx& columnName, const StringEx& nullValue /* = "" */)
{
  int columnIndex = FindColumnIndex(columnName);
  return GetString(columnIndex, nullValue);
}

const unsigned char* SQLite3ResultSet::GetBlob(int columnIndex, int& len)
{
  CheckStmt();

  if (columnIndex < 0 || columnIndex > m_cols-1)
  {
    throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_INVALID_INDEX);
  }

  len = sqlite3_column_bytes((sqlite3_stmt*) m_stmt, columnIndex);
  return (const unsigned char*) sqlite3_column_blob((sqlite3_stmt*) m_stmt, columnIndex);
}

const unsigned char* SQLite3ResultSet::GetBlob(const StringEx& columnName, int& len)
{
  int columnIndex = FindColumnIndex(columnName);
  return GetBlob(columnIndex, len);
}

MemoryBuffer& SQLite3ResultSet::GetBlob(int columnIndex, MemoryBuffer& buffer)
{
  CheckStmt();

  if (columnIndex < 0 || columnIndex > m_cols-1)
  {
    throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_INVALID_INDEX);
  }

  int len = sqlite3_column_bytes((sqlite3_stmt*) m_stmt, columnIndex);
  const void* blob = sqlite3_column_blob((sqlite3_stmt*) m_stmt, columnIndex);
  buffer.AppendData((void*) blob, (size_t) len);
  return buffer;
}

MemoryBuffer& SQLite3ResultSet::GetBlob(const StringEx& columnName, MemoryBuffer& buffer)
{
  int columnIndex = FindColumnIndex(columnName);
  return GetBlob(columnIndex, buffer);
}

bool SQLite3ResultSet::GetBool(int columnIndex)
{
  return GetInt(columnIndex) != 0;
}

bool SQLite3ResultSet::GetBool(const StringEx& columnName)
{
  int columnIndex = FindColumnIndex(columnName);
  return GetBool(columnIndex);
}

bool SQLite3ResultSet::IsNull(int columnIndex)
{
  return (GetColumnType(columnIndex) == SQLITE_NULL);
}

bool SQLite3ResultSet::IsNull(const StringEx& columnName)
{
  int columnIndex = FindColumnIndex(columnName);
  return (GetColumnType(columnIndex) == SQLITE_NULL);
}

int SQLite3ResultSet::FindColumnIndex(const StringEx& columnName)
{
  CheckStmt();

  if (columnName.length() > 0)
  {
    for (int columnIndex = 0; columnIndex < m_cols; columnIndex++)
    {
      const char* temp = sqlite3_column_name((sqlite3_stmt*) m_stmt, columnIndex);

      if (strcmp(columnName.c_str(), temp) == 0)
      {
        return columnIndex;
      }
    }
  }

  throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_INVALID_INDEX);
}

StringEx SQLite3ResultSet::GetColumnName(int columnIndex)
{
  CheckStmt();

  if (columnIndex < 0 || columnIndex > m_cols-1)
  {
    throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_INVALID_INDEX);
  }

  const char* localValue = sqlite3_column_name((sqlite3_stmt*) m_stmt, columnIndex);
  return localValue;
}

StringEx SQLite3ResultSet::GetDeclaredColumnType(int columnIndex)
{
  CheckStmt();

  if (columnIndex < 0 || columnIndex > m_cols-1)
  {
    throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_INVALID_INDEX);
  }

  const char* localValue = sqlite3_column_decltype((sqlite3_stmt*) m_stmt, columnIndex);
  return localValue;
}

int SQLite3ResultSet::GetColumnType(int columnIndex)
{
  CheckStmt();

  if (columnIndex < 0 || columnIndex > m_cols-1)
  {
    throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_INVALID_INDEX);
  }

  return sqlite3_column_type((sqlite3_stmt*) m_stmt, columnIndex);
}

bool SQLite3ResultSet::Eof()
{
  CheckStmt();
  return m_eof;
}

bool SQLite3ResultSet::NextRow()
{
  CheckStmt();

  int rc;
  if (m_first)
  {
    m_first = false;
    rc = (m_eof) ? SQLITE_DONE : SQLITE_ROW;
  }
  else
  {
    rc = sqlite3_step((sqlite3_stmt*) m_stmt);
  }

  if (rc == SQLITE_DONE) // no more rows
  {
    m_eof = true;
    return false;
  }
  else if (rc == SQLITE_ROW) // more rows
  {
    return true;
  }
  else
  {
    rc = sqlite3_finalize((sqlite3_stmt*) m_stmt);
    m_stmt = 0;
    throw SQLite3Exception(rc, sqlite3_errmsg((sqlite3*) m_db));
  }
}

void SQLite3ResultSet::Finalize()
{
  if (m_stmt && m_ownStmt)
  {
    int rc = sqlite3_finalize((sqlite3_stmt*) m_stmt);
    m_stmt = 0;
    if (rc != SQLITE_OK)
    {
      throw SQLite3Exception(rc, sqlite3_errmsg((sqlite3*) m_db));
    }
  }
}

StringEx SQLite3ResultSet::GetSQL()
{
  StringEx sqlString = "";
#if SQLITE_VERSION_NUMBER >= 3005003
  CheckStmt();
  const char* sqlLocal = sqlite3_sql((sqlite3_stmt*) m_stmt);
  if (sqlLocal != NULL) sqlString = sqlLocal;
#endif
  return sqlString;
}

bool SQLite3ResultSet::IsOk()
{
  return (m_db != 0) && (m_stmt != 0);
}

void SQLite3ResultSet::CheckStmt()
{
  if (m_stmt == 0)
  {
    throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_NOSTMT);
  }
}

StringEx SQLite3ResultSet::GetDatabaseName(int columnIndex)
{
#if SQLITE3EX3_HAVE_METADATA
  CheckStmt();
  if (columnIndex < 0 || columnIndex > m_cols-1)
  {
    throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_INVALID_INDEX);
  }

  const char* localValue = sqlite3_column_database_name((sqlite3_stmt*) m_stmt, columnIndex);
  if (localValue != NULL)
    return localValue;
  else
    return NULL;
#else
  throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_NOMETADATA);
#endif
}

StringEx SQLite3ResultSet::GetTableName(int columnIndex)
{
#if SQLITE3EX3_HAVE_METADATA
  CheckStmt();
  if (columnIndex < 0 || columnIndex > m_cols-1)
  {
    throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_INVALID_INDEX);
  }

  const char* localValue = sqlite3_column_table_name((sqlite3_stmt*) m_stmt, columnIndex);
  if (localValue != NULL)
    return localValue;
  else
    return NULL;
#else
  throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_NOMETADATA);
#endif
}

StringEx SQLite3ResultSet::GetOriginName(int columnIndex)
{
#if SQLITE3EX3_HAVE_METADATA
  CheckStmt();
  if (columnIndex < 0 || columnIndex > m_cols-1)
  {
    throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_INVALID_INDEX);
  }

  const char* localValue = sqlite3_column_origin_name((sqlite3_stmt*) m_stmt, columnIndex);
  if (localValue != NULL)
    return localValue;
  else
    return NULL;
#else
  throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_NOMETADATA);
#endif
}

// ----------------------------------------------------------------------------
// SQLite3Table: class holding the complete result set of a query
// ----------------------------------------------------------------------------

SQLite3Table::SQLite3Table()
{
  m_results = 0;
  m_rows = 0;
  m_cols = 0;
  m_currentRow = 0;
}

SQLite3Table::SQLite3Table(const SQLite3Table& table)
{
  m_results = table.m_results;
  // Only one object can own the results
  const_cast<SQLite3Table&>(table).m_results = 0;
  m_rows = table.m_rows;
  m_cols = table.m_cols;
  m_currentRow = table.m_currentRow;
}

SQLite3Table::SQLite3Table(char** results, int rows, int cols)
{
  m_results = results;
  m_rows = rows;
  m_cols = cols;
  m_currentRow = 0;
}

SQLite3Table::~SQLite3Table()
{
  try
  {
    Finalize();
  }
  catch (...)
  {
  }
}

SQLite3Table& SQLite3Table::operator=(const SQLite3Table& table)
{
  if (this != &table)
  {
    try
    {
      Finalize();
    }
    catch (...)
    {
    }
    m_results = table.m_results;
    // Only one object can own the results
    const_cast<SQLite3Table&>(table).m_results = 0;
    m_rows = table.m_rows;
    m_cols = table.m_cols;
    m_currentRow = table.m_currentRow;
  }
  return *this;
}

void SQLite3Table::Finalize()
{
  if (m_results)
  {
    sqlite3_free_table(m_results);
    m_results = 0;
  }
}

int SQLite3Table::GetColumnCount()
{
  CheckResults();
  return m_cols;
}

int SQLite3Table::GetRowCount()
{
  CheckResults();
  return m_rows;
}

int SQLite3Table::FindColumnIndex(const StringEx& columnName)
{
  CheckResults();
  if (columnName.length() > 0)
  {
    for (int columnIndex = 0; columnIndex < m_cols; columnIndex++)
    {
      if (strcmp(columnName.c_str(), m_results[columnIndex]) == 0)
      {
        return columnIndex;
      }
    }
  }

  throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_INVALID_NAME);
}

StringEx SQLite3Table::GetAsString(int columnIndex)
{
  if (columnIndex < 0 || columnIndex > m_cols-1)
  {
    throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_INVALID_INDEX);
  }

  int nIndex = (m_currentRow*m_cols) + m_cols + columnIndex;
  const char* localValue = m_results[nIndex];
  return localValue;
}

StringEx SQLite3Table::GetAsString(const StringEx& columnName)
{
  int index = FindColumnIndex(columnName);
  return GetAsString(index);
}

int SQLite3Table::GetInt(int columnIndex, int nullValue /* = 0 */)
{
  if (IsNull(columnIndex))
  {
    return nullValue;
  }
  else
  {
    return (int) ConvertStringToLong(GetAsString(columnIndex),nullValue);
  }
}

int SQLite3Table::GetInt(const StringEx& columnName, int nullValue /* = 0 */)
{
  if (IsNull(columnName))
  {
    return nullValue;
  }
  else
  {
    return (int) ConvertStringToLong(GetAsString(columnName),nullValue);
  }
}

Int64Ex SQLite3Table::GetInt64(int columnIndex, Int64Ex nullValue /* = 0 */)
{
  if (IsNull(columnIndex))
  {
    return nullValue;
  }
  else
  {
    return ConvertStringToLongLong(GetAsString(columnIndex), nullValue);
  }
}

Int64Ex SQLite3Table::GetInt64(const StringEx& columnName, Int64Ex nullValue /* = 0 */)
{
  if (IsNull(columnName))
  {
    return nullValue;
  }
  else
  {
    return ConvertStringToLongLong(GetAsString(columnName), nullValue);
  }
}

// Since SQLite uses internally a locale independent StringEx representation
// of double values, we need to provide our own conversion procedure using
// always a point as the decimal separator.
// The following code duplicates a SQLite utility function with minor modifications.

static double SQLite3AtoF(const char *z)
{
  int sign = 1;
  long double v1 = 0.0;
  int nSignificant = 0;
  while (isspace(*(unsigned char*)z))
  {
    ++z;
  }
  if (*z == '-')
  {
    sign = -1;
    ++z;
  }
  else if (*z == '+')
  {
    ++z;
  }
  while (*z == '0')
  {
    ++z;
  }
  while (isdigit(*(unsigned char*)z))
  {
    v1 = v1*10.0 + (*z - '0');
    ++z;
    ++nSignificant;
  }
  if (*z == '.')
  {
    long double divisor = 1.0;
    ++z;
    if (nSignificant == 0)
    {
      while (*z == '0')
      {
        divisor *= 10.0;
        ++z;
      }
    }
    while (isdigit(*(unsigned char*)z))
    {
      if (nSignificant < 18)
      {
        v1 = v1*10.0 + (*z - '0');
        divisor *= 10.0;
        ++nSignificant;
      }
      ++z;
    }
    v1 /= divisor;
  }
  if (*z=='e' || *z=='E')
  {
    int esign = 1;
    int eval = 0;
    long double scale = 1.0;
    ++z;
    if (*z == '-')
    {
      esign = -1;
      ++z;
    }
    else if (*z == '+')
    {
      ++z;
    }
    while (isdigit(*(unsigned char*)z))
    {
      eval = eval*10 + *z - '0';
      ++z;
    }
    while (eval >= 64) { scale *= 1.0e+64; eval -= 64; }
    while (eval >= 16) { scale *= 1.0e+16; eval -= 16; }
    while (eval >=  4) { scale *= 1.0e+4;  eval -= 4; }
    while (eval >=  1) { scale *= 1.0e+1;  eval -= 1; }
    if (esign < 0)
    {
      v1 /= scale;
    }
    else
    {
      v1 *= scale;
    }
  }
  return (double) ((sign < 0) ? -v1 : v1);
}

double SQLite3Table::GetDouble(int columnIndex, double nullValue /* = 0.0 */)
{
  if (IsNull(columnIndex))
  {
    return nullValue;
  }
  else
  {
    if (columnIndex < 0 || columnIndex > m_cols-1)
    {
      throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_INVALID_INDEX);
    }
    int nIndex = (m_currentRow*m_cols) + m_cols + columnIndex;
    return SQLite3AtoF(m_results[nIndex]);
  }
}

double SQLite3Table::GetDouble(const StringEx& columnName, double nullValue /* = 0.0 */)
{
  int index = FindColumnIndex(columnName);
  return GetDouble(index, nullValue);
}

StringEx SQLite3Table::GetString(int columnIndex, const StringEx& nullValue /* = "" */)
{
  if (IsNull(columnIndex))
  {
    return nullValue;
  }
  else
  {
    return GetAsString(columnIndex);
  }
}

StringEx SQLite3Table::GetString(const StringEx& columnName, const StringEx& nullValue /* = "" */)
{
  if (IsNull(columnName))
  {
    return nullValue;
  }
  else
  {
    return GetAsString(columnName);
  }
}

bool SQLite3Table::GetBool(int columnIndex)
{
  return GetInt(columnIndex) != 0;
}

bool SQLite3Table::GetBool(const StringEx& columnName)
{
  int columnIndex = FindColumnIndex(columnName);
  return GetBool(columnIndex);
}

bool SQLite3Table::IsNull(int columnIndex)
{
  CheckResults();

  if (columnIndex < 0 || columnIndex > m_cols-1)
  {
    throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_INVALID_INDEX);
  }

  int index = (m_currentRow*m_cols) + m_cols + columnIndex;
  const char* localValue = m_results[index];
  return (localValue == 0);
}

bool SQLite3Table::IsNull(const StringEx& columnName)
{
  int index = FindColumnIndex(columnName);
  return IsNull(index);
}

StringEx SQLite3Table::GetColumnName(int columnIndex)
{
  CheckResults();

  if (columnIndex < 0 || columnIndex > m_cols-1)
  {
    throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_INVALID_INDEX);
  }

  const char* localValue = m_results[columnIndex];
  return localValue;
}

void SQLite3Table::SetRow(int row)
{
  CheckResults();

  if (row < 0 || row > m_rows-1)
  {
    throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_INVALID_ROW);
  }

  m_currentRow = row;
}

bool SQLite3Table::IsOk()
{
  return (m_results != 0);
}

void SQLite3Table::CheckResults()
{
  if (m_results == 0)
  {
    throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_NORESULT);
  }
}

// ----------------------------------------------------------------------------
// SQLite3Statement: class holding a prepared statement
// ----------------------------------------------------------------------------

SQLite3Statement::SQLite3Statement()
{
  m_db = 0;
  m_stmt = 0;
  m_hasOwnership = false;
}

SQLite3Statement::SQLite3Statement(const SQLite3Statement& statement)
{
  m_db = statement.m_db;
  m_stmt = statement.m_stmt;
  m_hasOwnership = statement.m_hasOwnership;
  // Only one object can own prepared statement
  const_cast<SQLite3Statement&>(statement).m_hasOwnership = false;
}

SQLite3Statement::SQLite3Statement(void* db, void* stmt)
{
  m_db = db;
  m_stmt = stmt;
  m_hasOwnership = true;
}

SQLite3Statement::~SQLite3Statement()
{
  try
  {
    Finalize();
  }
  catch (...)
  {
  }
}

SQLite3Statement& SQLite3Statement::operator=(const SQLite3Statement& statement)
{
  if (this != &statement)
  {
    try
    {
      Finalize();
    }
    catch (...)
    {
    }
    m_db = statement.m_db;
    m_stmt = statement.m_stmt;
    m_hasOwnership = statement.m_hasOwnership;
    // Only one object can own prepared statement
    const_cast<SQLite3Statement&>(statement).m_hasOwnership = false;
  }
  return *this;
}

int SQLite3Statement::ExecuteUpdate()
{
  CheckDatabase();
  CheckStmt();

  const char* localError=0;

  int rc = sqlite3_step((sqlite3_stmt*) m_stmt);

  if (rc == SQLITE_DONE)
  {
    int rowsChanged = sqlite3_changes((sqlite3*) m_db);

    rc = sqlite3_reset((sqlite3_stmt*) m_stmt);

    if (rc != SQLITE_OK)
    {
      localError = sqlite3_errmsg((sqlite3*) m_db);
      throw SQLite3Exception(rc, localError);
    }

    return rowsChanged;
  }
  else
  {
    rc = sqlite3_reset((sqlite3_stmt*) m_stmt);
    localError = sqlite3_errmsg((sqlite3*) m_db);
    throw SQLite3Exception(rc, localError);
  }
}

SQLite3ResultSet SQLite3Statement::ExecuteQuery(bool transferStatementOwnership)
{
  CheckDatabase();
  CheckStmt();
  if (transferStatementOwnership)
  {
    if (m_hasOwnership)
    {
      m_hasOwnership = false;
    }
    else
    {
      throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_NOTOWNED);
    }
  }

  int rc = sqlite3_step((sqlite3_stmt*) m_stmt);

  if (rc == SQLITE_DONE)  // no more rows
  {
    return SQLite3ResultSet(m_db, m_stmt, true/*eof*/, true/*first*/, transferStatementOwnership);
  }
  else if (rc == SQLITE_ROW)  // one or more rows
  {
    return SQLite3ResultSet(m_db, m_stmt, false/*eof*/, true/*first*/, transferStatementOwnership);
  }
  else
  {
    rc = sqlite3_reset((sqlite3_stmt*) m_stmt);
    const char* localError = sqlite3_errmsg((sqlite3*) m_db);
    throw SQLite3Exception(rc, localError);
  }
}

int SQLite3Statement::GetParamCount()
{
  CheckStmt();
  return sqlite3_bind_parameter_count((sqlite3_stmt*) m_stmt);
}

int SQLite3Statement::GetParamIndex(const StringEx& paramName)
{
  CheckStmt();
  return sqlite3_bind_parameter_index((sqlite3_stmt*) m_stmt, paramName.c_str());
}

StringEx SQLite3Statement::GetParamName(int paramIndex)
{
  CheckStmt();
  return sqlite3_bind_parameter_name((sqlite3_stmt*) m_stmt, paramIndex);
}

void SQLite3Statement::Bind(int paramIndex, const StringEx& StringExValue)
{
  CheckStmt();

  int rc = sqlite3_bind_text((sqlite3_stmt*) m_stmt, paramIndex, StringExValue.c_str(), -1, SQLITE_TRANSIENT);

  if (rc != SQLITE_OK)
  {
    throw SQLite3Exception(rc, ERRMSG_BIND_STR);
  }
}

void SQLite3Statement::Bind(int paramIndex, int intValue)
{
  CheckStmt();
  int rc = sqlite3_bind_int((sqlite3_stmt*) m_stmt, paramIndex, intValue);

  if (rc != SQLITE_OK)
  {
    throw SQLite3Exception(rc, ERRMSG_BIND_INT);
  }
}

void SQLite3Statement::Bind(int paramIndex, Int64Ex int64Value)
{
  CheckStmt();
  int rc = sqlite3_bind_int64((sqlite3_stmt*) m_stmt, paramIndex, int64Value);

  if (rc != SQLITE_OK)
  {
    throw SQLite3Exception(rc, ERRMSG_BIND_INT64);
  }
}

void SQLite3Statement::Bind(int paramIndex, double doubleValue)
{
  CheckStmt();
  int rc = sqlite3_bind_double((sqlite3_stmt*) m_stmt, paramIndex, doubleValue);

  if (rc != SQLITE_OK)
  {
    throw SQLite3Exception(rc, ERRMSG_BIND_DBL);
  }
}

void SQLite3Statement::Bind(int paramIndex, const char* charValue)
{
  CheckStmt();
  int rc = sqlite3_bind_text((sqlite3_stmt*) m_stmt, paramIndex, charValue, -1, SQLITE_TRANSIENT);

  if (rc != SQLITE_OK)
  {
    throw SQLite3Exception(rc, ERRMSG_BIND_STR);
  }
}

void SQLite3Statement::Bind(int paramIndex, const unsigned char* blobValue, int blobLen)
{
  CheckStmt();
  int rc = sqlite3_bind_blob((sqlite3_stmt*) m_stmt, paramIndex,
                             (const void*)blobValue, blobLen, SQLITE_TRANSIENT);

  if (rc != SQLITE_OK)
  {
    throw SQLite3Exception(rc, ERRMSG_BIND_BLOB);
  }
}

void SQLite3Statement::Bind(int paramIndex, const MemoryBuffer& blobValue)
{
  CheckStmt();
  int blobLen = (int) blobValue.GetDataLen();
  int rc = sqlite3_bind_blob((sqlite3_stmt*) m_stmt, paramIndex,
                             (const void*)blobValue.GetData(), blobLen, SQLITE_TRANSIENT);

  if (rc != SQLITE_OK)
  {
    throw SQLite3Exception(rc, ERRMSG_BIND_BLOB);
  }
}

void SQLite3Statement::BindBool(int paramIndex, bool value)
{
  Bind(paramIndex, value ? 1 : 0);
}

void SQLite3Statement::BindNull(int paramIndex)
{
  CheckStmt();
  int rc = sqlite3_bind_null((sqlite3_stmt*) m_stmt, paramIndex);

  if (rc != SQLITE_OK)
  {
    throw SQLite3Exception(rc, ERRMSG_BIND_NULL);
  }
}

void SQLite3Statement::BindZeroBlob(int paramIndex, int blobSize)
{
#if SQLITE_VERSION_NUMBER >= 3004000
  CheckStmt();
  int rc = sqlite3_bind_zeroblob((sqlite3_stmt*) m_stmt, paramIndex, blobSize);
  if (rc != SQLITE_OK)
  {
    throw SQLite3Exception(rc, ERRMSG_BIND_ZEROBLOB);
  }
#else
  throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_NOINCBLOB);
#endif
}

void SQLite3Statement::ClearBindings()
{
  CheckStmt();
#if 0 // missing in SQLite DLL
  int rc = sqlite3_clear_bindings((sqlite3_stmt*) m_stmt);

  if (rc != SQLITE_OK)
  {
    throw SQLite3Exception(rc, ERRMSG_BIND_CLEAR);
  }
#else
  for (int paramIndex = 1; paramIndex <= GetParamCount(); paramIndex++)
  {
    BindNull(paramIndex);
  }
#endif
}

StringEx SQLite3Statement::GetSQL()
{
  StringEx sqlString = "";
#if SQLITE_VERSION_NUMBER >= 3005003
  CheckStmt();
  const char* sqlLocal = sqlite3_sql((sqlite3_stmt*) m_stmt);
  if (sqlLocal != NULL) sqlString = sqlLocal;
#endif
  return sqlString;
}

void SQLite3Statement::Reset()
{
  if (m_stmt)
  {
    int rc = sqlite3_reset((sqlite3_stmt*) m_stmt);

    if (rc != SQLITE_OK)
    {
      const char* localError = sqlite3_errmsg((sqlite3*) m_db);
      throw SQLite3Exception(rc, localError);
    }
  }
}

void SQLite3Statement::Finalize()
{
  if (m_stmt && m_hasOwnership)
  {
    int rc = sqlite3_finalize((sqlite3_stmt*) m_stmt);
    m_stmt = 0;
    m_hasOwnership = false;

    if (rc != SQLITE_OK)
    {
      const char* localError = sqlite3_errmsg((sqlite3*) m_db);
      throw SQLite3Exception(rc, localError);
    }
  }
}

bool SQLite3Statement::IsOk()
{
  return (m_db != 0) && (m_stmt != 0);
}

void SQLite3Statement::CheckDatabase()
{
  if (m_db == 0)
  {
    throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_NODB);
  }
}

void SQLite3Statement::CheckStmt()
{
  if (m_stmt == 0)
  {
    throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_NOSTMT);
  }
}

//

SQLite3Blob::SQLite3Blob()
{
  m_db   = NULL;
  m_blob = NULL;
  m_ok   = false;
  m_writable = false;
}

SQLite3Blob::SQLite3Blob(const SQLite3Blob& blob)
{
  m_db   = blob.m_db;
  m_blob = blob.m_blob;
  m_ok   = blob.m_ok;
  m_writable = blob.m_writable;
}

SQLite3Blob& SQLite3Blob::operator=(const SQLite3Blob& blob)
{
  if (this != &blob)
  {
    try
    {
      Finalize();
    }
    catch (...)
    {
    }
    m_db   = blob.m_db;
    m_blob = blob.m_blob;
    m_ok   = blob.m_ok;
    m_writable = blob.m_writable;
    // only one blob can own the blob handle
    const_cast<SQLite3Blob&>(blob).m_ok = false;
  }
  return *this;
}

SQLite3Blob::SQLite3Blob(void* db, void* blobHandle, bool writable)
{
  m_db   = db;
  m_blob = blobHandle;
  m_ok   = true;
  m_writable = writable;
}

SQLite3Blob::~SQLite3Blob()
{
  try
  {
    Finalize();
  }
  catch (...)
  {
  }
}

MemoryBuffer& SQLite3Blob::Read(MemoryBuffer& blobValue, int length, int offset)
{
#if SQLITE_VERSION_NUMBER >= 3004000
  CheckBlob();
  char* localBuffer = (char*) blobValue.GetAppendBuf((size_t) length);
  int rc = sqlite3_blob_read((sqlite3_blob*) m_blob, localBuffer, length, offset);

  if (rc != SQLITE_OK)
  {
    const char* localError = sqlite3_errmsg((sqlite3*) m_db);
    throw SQLite3Exception(rc, localError);
  }

  blobValue.UngetAppendBuf((size_t) length);
#else
  throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_NOINCBLOB);
#endif
  return blobValue;
}

void SQLite3Blob::Write(const MemoryBuffer& blobValue, int offset)
{
#if SQLITE_VERSION_NUMBER >= 3004000
  CheckBlob();
  if (m_writable)
  {
    int blobLen = (int) blobValue.GetDataLen();
    int rc = sqlite3_blob_write((sqlite3_blob*) m_blob,
                                (const void*) blobValue.GetData(), blobLen, offset);

    if (rc != SQLITE_OK)
    {
      const char* localError = sqlite3_errmsg((sqlite3*) m_db);
      throw SQLite3Exception(rc, localError);
    }
  }
  else
  {
    throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_INVALID_BLOB);
  }
#else
  throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_NOINCBLOB);
#endif
}

bool SQLite3Blob::IsOk()
{
  return m_ok;
}

bool SQLite3Blob::IsReadOnly()
{
  return !m_writable;
}

int SQLite3Blob::GetSize()
{
#if SQLITE_VERSION_NUMBER >= 3004000
  CheckBlob();
  return sqlite3_blob_bytes((sqlite3_blob*) m_blob);
#else
  throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_NOINCBLOB);
  return 0;
#endif
}

void SQLite3Blob::Finalize()
{
#if SQLITE_VERSION_NUMBER >= 3004000
  if (m_ok)
  {
    int rc = sqlite3_blob_close((sqlite3_blob*) m_blob);
    m_blob = NULL;
    m_ok = false;
    if (rc != SQLITE_OK)
    {
      const char* localError = sqlite3_errmsg((sqlite3*) m_db);
      throw SQLite3Exception(rc, localError);
    }
  }
#else
  throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_NOINCBLOB);
#endif
}

void SQLite3Blob::CheckBlob()
{
  if (!m_ok)
  {
    throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_INVALID_BLOB);
  }
}

// ----------------------------------------------------------------------------
// SQLite3Database: class holding a SQLite3 database object
// ----------------------------------------------------------------------------

bool SQLite3Database::ms_sharedCacheEnabled = false;

void
SQLite3Database::SetSharedCache(bool enable)
{
  int flag = (enable) ? 1 : 0;
  int rc = sqlite3_enable_shared_cache(flag);
  if (rc != SQLITE_OK)
  {
    throw SQLite3Exception(rc, ERRMSG_SHARED_CACHE);
  }
  ms_sharedCacheEnabled = enable;
}

#ifdef SQLITE_HAS_CODEC
bool SQLite3Database::ms_hasEncryptionSupport = true;
#else
bool SQLite3Database::ms_hasEncryptionSupport = false;
#endif

#if SQLITE3EX3_HAVE_METADATA
bool SQLite3Database::ms_hasMetaDataSupport = true;
#else
bool SQLite3Database::ms_hasMetaDataSupport = false;
#endif

#if SQLITE3EX3_HAVE_LOAD_EXTENSION
bool SQLite3Database::ms_hasLoadExtSupport = true;
#else
bool SQLite3Database::ms_hasLoadExtSupport = false;
#endif

#if SQLITE3EX3_USE_NAMED_COLLECTIONS
bool SQLite3Database::ms_hasNamedCollectionSupport = true;
#else
bool SQLite3Database::ms_hasNamedCollectionSupport = false;
#endif

#if SQLITE_VERSION_NUMBER >= 3004000
bool SQLite3Database::ms_hasIncrementalBlobSupport = true;
#else
bool SQLite3Database::ms_hasIncrementalBlobSupport = false;
#endif

#if SQLITE_VERSION_NUMBER >= 3006008
bool SQLite3Database::ms_hasSavepointSupport = true;
#else
bool SQLite3Database::ms_hasSavepointSupport = false;
#endif

#if SQLITE_VERSION_NUMBER >= 3006011
bool SQLite3Database::ms_hasBackupSupport = true;
#else
bool SQLite3Database::ms_hasBackupSupport = false;
#endif

#if SQLITE_VERSION_NUMBER >= 3007000
bool SQLite3Database::ms_hasWriteAheadLogSupport = true;
#else
bool SQLite3Database::ms_hasWriteAheadLogSupport = false;
#endif

bool
SQLite3Database::HasEncryptionSupport()
{
  return ms_hasEncryptionSupport;
}

bool
SQLite3Database::HasMetaDataSupport()
{
  return ms_hasMetaDataSupport;
}

bool
SQLite3Database::HasLoadExtSupport()
{
  return ms_hasLoadExtSupport;
}

bool
SQLite3Database::HasNamedCollectionSupport()
{
  return ms_hasNamedCollectionSupport;
}

bool
SQLite3Database::HasIncrementalBlobSupport()
{
  return ms_hasIncrementalBlobSupport;
}

bool
SQLite3Database::HasSavepointSupport()
{
  return ms_hasSavepointSupport;
}

bool
SQLite3Database::HasBackupSupport()
{
  return ms_hasBackupSupport;
}

bool
SQLite3Database::HasWriteAheadLogSupport()
{
  return ms_hasWriteAheadLogSupport;
}

SQLite3Database::SQLite3Database()
{
  m_db = 0;
  m_busyTimeoutMs = 60000; // 60 seconds
  m_isEncrypted = false;
}

SQLite3Database::SQLite3Database(const SQLite3Database& db)
{
  m_db = db.m_db;
  m_busyTimeoutMs = 60000; // 60 seconds
  m_isEncrypted = false;
}

SQLite3Database::~SQLite3Database()
{
  Close();
}

SQLite3Database& SQLite3Database::operator=(const SQLite3Database& db)
{
  if (this != &db)
  {
    if (m_db == 0)
    {
      m_db = db.m_db;
      m_busyTimeoutMs = 60000; // 60 seconds
      m_isEncrypted = db.m_isEncrypted;
    }
    else
    {
      throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_DBASSIGN_FAILED);
    }
  }
  return *this;
}

void SQLite3Database::Open(const StringEx& fileName, const StringEx& key, int flags)
{
  MemoryBuffer binaryKey;
  if (key.length() > 0)
  {
    binaryKey.AppendData((void*)(key.c_str()), key.length());
  }
  Open(fileName, binaryKey, flags);
}

void SQLite3Database::Open(const StringEx& fileName, const MemoryBuffer& key, int flags)
{
  int rc = sqlite3_open_v2((const char*) fileName.c_str(), (sqlite3**) &m_db, flags, NULL);

  if (rc != SQLITE_OK)
  {
    const char* localError = sqlite3_errmsg((sqlite3*) m_db);
    Close();
    throw SQLite3Exception(rc, localError);
  }

  rc = sqlite3_extended_result_codes((sqlite3*) m_db, 1);
  if (rc != SQLITE_OK)
  {
    const char* localError = sqlite3_errmsg((sqlite3*) m_db);
    Close();
    throw SQLite3Exception(rc, localError);
  }

#ifdef SQLITE_HAS_CODEC
  if (key.GetDataLen() > 0)
  {
    rc = sqlite3_key((sqlite3*) m_db, key.GetData(), (int) key.GetDataLen());
    if (rc != SQLITE_OK)
    {
      const char* localError = sqlite3_errmsg((sqlite3*) m_db);
      Close();
      throw SQLite3Exception(rc, localError);
    }
    m_isEncrypted = true;
  }
#else
#endif

  SetBusyTimeout(m_busyTimeoutMs);
}

bool SQLite3Database::IsOpen() const
{
  return (m_db != NULL);
}

void SQLite3Database::Close()
{
  if (m_db)
  {
#if SQLITE_VERSION_NUMBER >= 3006000
// Unfortunately the following code leads to a crash if the RTree module is used
// therefore it is disabled for now
#if 0
    // Finalize all unfinalized prepared statements
    sqlite3_stmt *pStmt;
    while( (pStmt = sqlite3_next_stmt((sqlite3*) m_db, 0))!=0 )
    {
      sqlite3_finalize(pStmt);
    }
#endif
#endif
    sqlite3_close((sqlite3*) m_db);
    m_db = 0;
    m_isEncrypted = false;
  }
}

void SQLite3Database::Backup(const StringEx& targetFileName, const StringEx& key, const StringEx& sourceDatabaseName)
{
  MemoryBuffer binaryKey;
  if (key.length() > 0)
  {
    binaryKey.AppendData((void*)(key.c_str()), key.length());
  }
  Backup(targetFileName, binaryKey, sourceDatabaseName);
}

void SQLite3Database::Backup(const StringEx& targetFileName, const MemoryBuffer& key, const StringEx& sourceDatabaseName)
{
#if SQLITE_VERSION_NUMBER >= 3006011
  CheckDatabase();

  sqlite3* pDest;
  sqlite3_backup* pBackup;
  int rc;
  rc = sqlite3_open(targetFileName.c_str(), &pDest);
  if (rc != SQLITE_OK)
  {
    sqlite3_close(pDest);
    throw SQLite3Exception(rc, ERRMSG_DBOPEN_FAILED);
  }
#ifdef SQLITE_HAS_CODEC
  if (key.GetDataLen() > 0)
  {
    rc = sqlite3_key(pDest, key.GetData(), (int) key.GetDataLen());
    if (rc != SQLITE_OK)
    {
      const char* localError = sqlite3_errmsg((sqlite3*) pDest);
      sqlite3_close(pDest);
      throw SQLite3Exception(rc, localError);
    }
  }
#else
#endif

  pBackup = sqlite3_backup_init(pDest, "main", (sqlite3*) m_db, sourceDatabaseName.c_str());
  if (pBackup == 0)
  {
    const char* localError = sqlite3_errmsg(pDest);
    sqlite3_close(pDest);
    throw SQLite3Exception(rc, localError);
  }

  do
  {
    rc = sqlite3_backup_step(pBackup, 10);
#if 0
    xProgress(sqlite3_backup_remaining(pBackup),
              sqlite3_backup_pagecount(pBackup));
#endif
    if (rc == SQLITE_BUSY || rc == SQLITE_LOCKED)
    {
      sqlite3_sleep(250);
    }
  }
  while (rc == SQLITE_OK || rc == SQLITE_BUSY || rc == SQLITE_LOCKED);

  sqlite3_backup_finish(pBackup);
  if (rc == SQLITE_DONE)
  {
    sqlite3_close(pDest);
  }
  else
  {
    const char* localError = sqlite3_errmsg(pDest);
    sqlite3_close(pDest);
    throw SQLite3Exception(rc, localError);
  }
#else
  throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_NOBACKUP);
#endif
}

void SQLite3Database::Restore(const StringEx& sourceFileName, const StringEx& key, const StringEx& targetDatabaseName)
{
  MemoryBuffer binaryKey;
  if (key.length() > 0)
  {
    binaryKey.AppendData((void*)(key.c_str()), key.length());
  }
  Restore(sourceFileName, binaryKey, targetDatabaseName);
}

void SQLite3Database::Restore(const StringEx& sourceFileName, const MemoryBuffer& key, const StringEx& targetDatabaseName)
{
#if SQLITE_VERSION_NUMBER >= 3006011
  CheckDatabase();

  sqlite3* pSrc;
  sqlite3_backup* pBackup;
  int rc;
  int nTimeout = 0;

  rc = sqlite3_open(sourceFileName.c_str(), &pSrc);
  if (rc != SQLITE_OK)
  {
    sqlite3_close(pSrc);
    throw SQLite3Exception(rc, ERRMSG_DBOPEN_FAILED);
  }
#ifdef SQLITE_HAS_CODEC
  if (key.GetDataLen() > 0)
  {
    rc = sqlite3_key(pSrc, key.GetData(), (int) key.GetDataLen());
    if (rc != SQLITE_OK)
    {
      const char* localError = sqlite3_errmsg((sqlite3*) pSrc);
      sqlite3_close(pSrc);
      throw SQLite3Exception(rc, localError);
    }
  }
#else
#endif

  pBackup = sqlite3_backup_init((sqlite3*) m_db, targetDatabaseName.c_str(), pSrc, "main");
  if (pBackup == 0)
  {
    const char* localError = sqlite3_errmsg((sqlite3*) m_db);
    sqlite3_close(pSrc);
    throw SQLite3Exception(rc, localError);
  }
  while ((rc = sqlite3_backup_step(pBackup, 100)) == SQLITE_OK || rc == SQLITE_BUSY)
  {
    if (rc == SQLITE_BUSY)
    {
      if (nTimeout++ >= 3) break;
      sqlite3_sleep(100);
    }
  }
  sqlite3_backup_finish(pBackup);
  if (rc == SQLITE_DONE)
  {
    sqlite3_close(pSrc);
  }
  else if (rc == SQLITE_BUSY || rc == SQLITE_LOCKED)
  {
    sqlite3_close(pSrc);
    throw SQLite3Exception(rc, ERRMSG_SOURCEDB_BUSY);
  }
  else
  {
    const char* localError = sqlite3_errmsg(pSrc);
    sqlite3_close(pSrc);
    throw SQLite3Exception(rc, localError);
  }
#else
  throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_NOBACKUP);
#endif
}

void SQLite3Database::Begin(SQLite3TransactionType transactionType)
{
  //StringEx sql;
  stringstream sql;
  switch (transactionType)
  {
    case SQLITE3EX_TRANSACTION_DEFERRED:
      sql << _T("begin deferred transaction");
      break;
    case SQLITE3EX_TRANSACTION_IMMEDIATE:
      sql << _T("begin immediate transaction");
      break;
    case SQLITE3EX_TRANSACTION_EXCLUSIVE:
      sql << _T("begin exclusive transaction");
      break;
    default:
      sql << _T("begin transaction");
      break;
  }
  ExecuteUpdate(sql.str());
}

void SQLite3Database::Commit()
{
  ExecuteUpdate("commit transaction");
}

void SQLite3Database::Rollback(const StringEx& savepointName)
{
#if SQLITE_VERSION_NUMBER >= 3006008
  if (savepointName.empty())
  {
#endif
    ExecuteUpdate("rollback transaction");
#if SQLITE_VERSION_NUMBER >= 3006008
  }
  else
  {
    ExecuteUpdate(StringEx(_T("rollback transaction to savepoint "))+savepointName);
  }
#endif
}

bool SQLite3Database::GetAutoCommit()
{
  CheckDatabase();
  return sqlite3_get_autocommit((sqlite3*) m_db) != 0;
}

void SQLite3Database::Savepoint(const StringEx& savepointName)
{
#if SQLITE_VERSION_NUMBER >= 3006008
  ExecuteUpdate(StringEx(_T("savepoint "))+savepointName);
#else
  throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_NOSAVEPOINT);
#endif
}

void SQLite3Database::ReleaseSavepoint(const StringEx& savepointName)
{
#if SQLITE_VERSION_NUMBER >= 3006008
  ExecuteUpdate(StringEx(_T("release savepoint "))+savepointName);
#else
  throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_NOSAVEPOINT);
#endif
}

SQLite3Statement SQLite3Database::PrepareStatement(const StringEx& sql)
{
  return PrepareStatement(sql.c_str());
}

SQLite3Statement SQLite3Database::PrepareStatement(const SQLite3StatementBuffer& sql)
{
  return PrepareStatement((const char*) sql);
}

SQLite3Statement SQLite3Database::PrepareStatement(const char* sql)
{
  CheckDatabase();
  sqlite3_stmt* stmt = (sqlite3_stmt*) Prepare(sql);
  return SQLite3Statement(m_db, stmt);
}

bool SQLite3Database::TableExists(const StringEx& tableName, const StringEx& databaseName)
{
  StringEx sql;
  if (databaseName.empty())
  {
    sql = _T("select count(*) from sqlite_master where type='table' and name like ?");
  }
  else
  {
    sql = StringEx(_T("select count(*) from ")) + databaseName + StringEx(_T(".sqlite_master where type='table' and name like ?"));
  }
  SQLite3Statement stmt = PrepareStatement(sql);
  stmt.Bind(1, tableName);
  SQLite3ResultSet resultSet = stmt.ExecuteQuery();
  long value = ConvertStringToLong(resultSet.GetAsString(0),0);
  return (value > 0);
}

bool SQLite3Database::EnableForeignKeySupport(bool enable)
{
  if (enable)
  {
    ExecuteUpdate("PRAGMA foreign_keys=ON;");
  }
  else
  {
    ExecuteUpdate("PRAGMA foreign_keys=OFF;");
  }
  bool enabled = IsForeignKeySupportEnabled();
  return (enable && enabled) || (!enable && !enabled);
}

bool SQLite3Database::IsForeignKeySupportEnabled()
{
  bool enabled = false;
  SQLite3ResultSet resultSet = ExecuteQuery("PRAGMA foreign_keys;");
  if (resultSet.NextRow())
  {
    enabled = (resultSet.GetInt(0) == 1);
  }
  return enabled;
}

SQLite3JournalMode
SQLite3Database::SetJournalMode(SQLite3JournalMode journalMode, const StringEx& database)
{
  StringEx mode = ConvertJournalMode(journalMode);
  StringEx query = _T("PRAGMA ");
  if (!database.empty())
  {
    query += database;
    query += _T(".");
  }
  query += _T("journal_mode=");
  query += mode;
  query += _T(";");
  SQLite3ResultSet resultSet = ExecuteQuery(query);
  if (resultSet.NextRow())
  {
    mode = resultSet.GetString(0);
  }
  return ConvertJournalMode(mode);
}

SQLite3JournalMode
SQLite3Database::GetJournalMode(const StringEx& database)
{
  StringEx mode = _T("DELETE");
  StringEx query = _T("PRAGMA ");
  if (!database.empty())
  {
    query += database;
    query += _T(".");
  }
  query += _T("journal_mode;");
  SQLite3ResultSet resultSet = ExecuteQuery(query);
  if (resultSet.NextRow())
  {
    mode = resultSet.GetString(0);
  }
  return ConvertJournalMode(mode);
}

/* static */
StringEx SQLite3Database::ConvertJournalMode(SQLite3JournalMode mode)
{
  StringEx journalMode;
  if      (mode == SQLITE3EX_JOURNALMODE_DELETE)   journalMode = _T("DELETE");
  else if (mode == SQLITE3EX_JOURNALMODE_PERSIST)  journalMode = _T("PERSIST");
  else if (mode == SQLITE3EX_JOURNALMODE_OFF)      journalMode = _T("OFF");
  else if (mode == SQLITE3EX_JOURNALMODE_TRUNCATE) journalMode = _T("TRUNCATE");
  else if (mode == SQLITE3EX_JOURNALMODE_MEMORY)   journalMode = _T("MEMORY");
  else if (mode == SQLITE3EX_JOURNALMODE_WAL)      journalMode = _T("WAL");
  else                                            journalMode = _T("DELETE");
  return journalMode;
}

/* static */
SQLite3JournalMode SQLite3Database::ConvertJournalMode(const StringEx& mode)
{
  SQLite3JournalMode journalMode;

  if      (Toupper(mode).compare(_T("DELETE")))   journalMode = SQLITE3EX_JOURNALMODE_DELETE;
  else if (Toupper(mode).compare(_T("PERSIST")))  journalMode = SQLITE3EX_JOURNALMODE_PERSIST;
  else if (Toupper(mode).compare(_T("OFF")))      journalMode = SQLITE3EX_JOURNALMODE_OFF;
  else if (Toupper(mode).compare(_T("TRUNCATE"))) journalMode = SQLITE3EX_JOURNALMODE_TRUNCATE;
  else if (Toupper(mode).compare(_T("MEMORY")))   journalMode = SQLITE3EX_JOURNALMODE_MEMORY;
  else if (Toupper(mode).compare(_T("WAL")))      journalMode = SQLITE3EX_JOURNALMODE_WAL;
  else                                     journalMode = SQLITE3EX_JOURNALMODE_DELETE;
  return journalMode;
}

bool SQLite3Database::CheckSyntax(const StringEx& sql)
{
  return CheckSyntax(sql.c_str());
}

bool SQLite3Database::CheckSyntax(const SQLite3StatementBuffer& sql)
{
  return CheckSyntax((const char*) sql);
}

bool SQLite3Database::CheckSyntax(const char* sql)
{
  return sqlite3_complete(sql) != 0;
}

int SQLite3Database::ExecuteUpdate(const StringEx& sql)
{
  return ExecuteUpdate(sql.c_str());
}

int SQLite3Database::ExecuteUpdate(const SQLite3StatementBuffer& sql)
{
  return ExecuteUpdate((const char*) sql);
}

int SQLite3Database::ExecuteUpdate(const char* sql)
{
  CheckDatabase();

  char* localError=0;

  int rc = sqlite3_exec((sqlite3*) m_db, sql, 0, 0, &localError);

  if (rc == SQLITE_OK)
  {
    return sqlite3_changes((sqlite3*) m_db);
  }
  else
  {
    throw SQLite3Exception(rc, localError);
  }
}

SQLite3ResultSet SQLite3Database::ExecuteQuery(const StringEx& sql)
{
  return ExecuteQuery(sql.c_str());
}

SQLite3ResultSet SQLite3Database::ExecuteQuery(const SQLite3StatementBuffer& sql)
{
  return ExecuteQuery((const char*) sql);
}

SQLite3ResultSet SQLite3Database::ExecuteQuery(const char* sql)
{
  CheckDatabase();

  sqlite3_stmt* stmt = (sqlite3_stmt*) Prepare(sql);

  int rc = sqlite3_step(stmt);

  if (rc == SQLITE_DONE) // no rows
  {
    return SQLite3ResultSet(m_db, stmt, true /* eof */);
  }
  else if (rc == SQLITE_ROW) // one or more rows
  {
    return SQLite3ResultSet(m_db, stmt, false /* eof */);
  }
  else
  {
    rc = sqlite3_finalize(stmt);
    const char* localError= sqlite3_errmsg((sqlite3*) m_db);
    throw SQLite3Exception(rc, localError);
  }
}

int SQLite3Database::ExecuteScalar(const StringEx& sql)
{
  return ExecuteScalar(sql.c_str());
}

int SQLite3Database::ExecuteScalar(const SQLite3StatementBuffer& sql)
{
  return ExecuteScalar((const char*) sql);
}

int SQLite3Database::ExecuteScalar(const char* sql)
{
  SQLite3ResultSet resultSet = ExecuteQuery(sql);

  if (resultSet.Eof() || resultSet.GetColumnCount() < 1)
  {
    throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_INVALID_QUERY);
  }

  return (int) ConvertStringToLong(resultSet.GetAsString(0),0);
}

SQLite3Table SQLite3Database::GetTable(const StringEx& sql)
{
  return GetTable(sql.c_str());
}

SQLite3Table SQLite3Database::GetTable(const SQLite3StatementBuffer& sql)
{
  return GetTable((const char*) sql);
}

SQLite3Table SQLite3Database::GetTable(const char* sql)
{
  CheckDatabase();

  char* localError=0;
  char** results=0;
  int rc;
  int rows(0);
  int cols(0);

  rc = sqlite3_get_table((sqlite3*) m_db, sql, &results, &rows, &cols, &localError);

  if (rc == SQLITE_OK)
  {
    return SQLite3Table(results, rows, cols);
  }
  else
  {
    throw SQLite3Exception(rc, localError);
  }
}

Int64Ex SQLite3Database::GetLastRowId()
{
  CheckDatabase();
  return Int64Ex(sqlite3_last_insert_rowid((sqlite3*) m_db));
}

SQLite3Blob SQLite3Database::GetReadOnlyBlob(Int64Ex rowId,
                                                 const StringEx& columnName,
                                                 const StringEx& tableName,
                                                 const StringEx& dbName)
{
  return GetBlob(rowId, columnName, tableName, dbName, false);
}

SQLite3Blob SQLite3Database::GetWritableBlob(Int64Ex rowId,
                                                 const StringEx& columnName,
                                                 const StringEx& tableName,
                                                 const StringEx& dbName)
{
  return GetBlob(rowId, columnName, tableName, dbName, true);
}

SQLite3Blob SQLite3Database::GetBlob(Int64Ex rowId,
                                         const StringEx& columnName,
                                         const StringEx& tableName,
                                         const StringEx& dbName,
                                         bool writable)
{
#if SQLITE_VERSION_NUMBER >= 3004000
  const char* localDbName = (!dbName.empty()) ? dbName.c_str() : (const char*) NULL;
  int flags = (writable) ? 1 : 0;
  sqlite3_blob* blobHandle;
  CheckDatabase();
  int rc = sqlite3_blob_open((sqlite3*) m_db, localDbName, tableName.c_str(), columnName.c_str(), rowId, flags, &blobHandle);
  if (rc != SQLITE_OK)
  {
    const char* localError = sqlite3_errmsg((sqlite3*) m_db);
    throw SQLite3Exception(rc, localError);
  }
  return SQLite3Blob(m_db, (void*) blobHandle, writable);
#else
  throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_NOINCBLOB);
  return NULL;
#endif
}

void SQLite3Database::Interrupt()
{
  CheckDatabase();
  sqlite3_interrupt((sqlite3*) m_db);
}

void SQLite3Database::SetBusyTimeout(int nMillisecs)
{
  CheckDatabase();
  m_busyTimeoutMs = nMillisecs;
  sqlite3_busy_timeout((sqlite3*) m_db, m_busyTimeoutMs);
}

StringEx SQLite3Database::GetVersion()
{
  return sqlite3_libversion();
}

StringEx SQLite3Database::GetSourceId()
{
#if SQLITE_VERSION_NUMBER >= 3006018
  return sqlite3_sourceid();
#else
  return NULL;
#endif
}

bool SQLite3Database::CompileOptionUsed(const StringEx& optionName)
{
#if SQLITE_VERSION_NUMBER >= 3006023
  return sqlite3_compileoption_used(optionName.c_str()) == 1;
#else
  return false;
#endif
}

StringEx SQLite3Database::GetCompileOptionName(int optionIndex)
{
#if SQLITE_VERSION_NUMBER >= 3006023
  const char* unknownOption = "";
  const char* optionName = sqlite3_compileoption_get(optionIndex);
  if (optionName == NULL)
  {
    optionName = unknownOption;
  }
  return optionName;
#else
  return NULL;
#endif
}

bool SQLite3Database::CreateFunction(const StringEx& funcName, int argCount, SQLite3ScalarFunction& function)
{
  CheckDatabase();
  int rc = sqlite3_create_function((sqlite3*) m_db, funcName.c_str(), argCount,
                                   SQLITE_UTF8, &function,
                                   (void (*)(sqlite3_context*,int,sqlite3_value**)) SQLite3FunctionContext::ExecScalarFunction, NULL, NULL);
  return rc == SQLITE_OK;
}

bool SQLite3Database::CreateFunction(const StringEx& funcName, int argCount, SQLite3AggregateFunction& function)
{
  CheckDatabase();
  int rc = sqlite3_create_function((sqlite3*) m_db, funcName.c_str(), argCount,
                                   SQLITE_UTF8, &function,
                                   NULL,
                                   (void (*)(sqlite3_context*,int,sqlite3_value**)) SQLite3FunctionContext::ExecAggregateStep,
                                   (void (*)(sqlite3_context*)) SQLite3FunctionContext::ExecAggregateFinalize);
  return rc == SQLITE_OK;
}

bool SQLite3Database::SetAuthorizer(SQLite3Authorizer& authorizer)
{
  CheckDatabase();
  int rc = sqlite3_set_authorizer((sqlite3*) m_db, SQLite3FunctionContext::ExecAuthorizer, &authorizer);
  return rc == SQLITE_OK;
}

void SQLite3Database::SetCommitHook(SQLite3Hook* commitHook)
{
  CheckDatabase();
  if (commitHook)
  {
    sqlite3_commit_hook((sqlite3*) m_db, (int(*)(void*)) SQLite3FunctionContext::ExecCommitHook, commitHook);
  }
  else
  {
    sqlite3_commit_hook((sqlite3*) m_db, (int(*)(void*)) NULL, NULL);
  }
}

void SQLite3Database::SetRollbackHook(SQLite3Hook* rollbackHook)
{
  CheckDatabase();
  if (rollbackHook)
  {
    sqlite3_rollback_hook((sqlite3*) m_db, (void(*)(void*)) SQLite3FunctionContext::ExecRollbackHook, rollbackHook);
  }
  else
  {
    sqlite3_rollback_hook((sqlite3*) m_db, (void(*)(void*)) NULL, NULL);
  }
}

void SQLite3Database::SetUpdateHook(SQLite3Hook* updateHook)
{
  CheckDatabase();
  if (updateHook)
  {
    sqlite3_update_hook((sqlite3*) m_db, (void(*)(void*,int,const char*,const char*, Int64Ex)) SQLite3FunctionContext::ExecUpdateHook, updateHook);
  }
  else
  {
    sqlite3_update_hook((sqlite3*) m_db, (void(*)(void*,int,const char*,const char*, Int64Ex)) NULL, NULL);
  }
}

void SQLite3Database::SetWriteAheadLogHook(SQLite3Hook* walHook)
{
#if SQLITE_VERSION_NUMBER >= 3007000
  CheckDatabase();
  if (walHook)
  {
    walHook->SetDatabase(this);
    sqlite3_wal_hook((sqlite3*) m_db, (int(*)(void *,sqlite3*,const char*,int)) SQLite3FunctionContext::ExecWriteAheadLogHook, walHook);
  }
  else
  {
    sqlite3_wal_hook((sqlite3*) m_db, (int(*)(void *,sqlite3*,const char*,int)) NULL, NULL);
  }
#else
  throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_NOWAL);
#endif
}

void SQLite3Database::WriteAheadLogCheckpoint(const StringEx& database)
{
#if SQLITE_VERSION_NUMBER >= 3007000
  CheckDatabase();
  int rc = sqlite3_wal_checkpoint((sqlite3*) m_db, database.c_str());

  if (rc != SQLITE_OK)
  {
    const char* localError = sqlite3_errmsg((sqlite3*) m_db);
    throw SQLite3Exception(rc, localError);
  }
#else
  throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_NOWAL);
#endif
}

void SQLite3Database::AutoWriteAheadLogCheckpoint(int frameCount)
{
#if SQLITE_VERSION_NUMBER >= 3007000
  CheckDatabase();
  int rc = sqlite3_wal_autocheckpoint((sqlite3*) m_db, frameCount);

  if (rc != SQLITE_OK)
  {
    const char* localError = sqlite3_errmsg((sqlite3*) m_db);
    throw SQLite3Exception(rc, localError);
  }
#else
  throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_NOWAL);
#endif
}

void SQLite3Database::SetCollation(const StringEx& collationName, SQLite3Collation* collation)
{
  CheckDatabase();
  int rc;
  if (collation)
  {
    rc = sqlite3_create_collation((sqlite3*) m_db, collationName.c_str(), SQLITE_UTF8, collation, (int(*)(void*,int,const void*,int,const void*)) SQLite3Database::ExecComparisonWithCollation);
  }
  else
  {
    rc = sqlite3_create_collation((sqlite3*) m_db, collationName.c_str(), SQLITE_UTF8, NULL, (int(*)(void*,int,const void*,int,const void*)) NULL);
  }
}

void SQLite3Database::SetCollationNeededCallback()
{
  CheckDatabase();
  int rc = sqlite3_collation_needed((sqlite3*) m_db, this, (void(*)(void*,sqlite3*,int,const char*)) SQLite3Database::ExecCollationNeeded);
  if (rc != SQLITE_OK)
  {
    const char* localError = sqlite3_errmsg((sqlite3*) m_db);
    throw SQLite3Exception(rc, localError);
  }
}

void SQLite3Database::CheckDatabase()
{
  if (!m_db)
  {
    throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_NODB);
  }
}

void* SQLite3Database::Prepare(const char* sql)
{
  CheckDatabase();

  const char* tail=0;
  sqlite3_stmt* stmt;

  int rc = sqlite3_prepare_v2((sqlite3*) m_db, sql, -1, &stmt, &tail);

  if (rc != SQLITE_OK)
  {
    const char* localError = sqlite3_errmsg((sqlite3*) m_db);
    throw SQLite3Exception(rc, localError);
  }

  return stmt;
}

/* static */
int SQLite3Database::ExecComparisonWithCollation(void* collation,
                                                   int len1, const void* text1,
                                                   int len2, const void* text2)
{
  char * lpText1 = (char *)malloc(len1);
  char * lpText2 = (char *)malloc(len2);
  memcpy(lpText1, text1, len1);
  memcpy(lpText2, text2, len2);
  lpText1[len1] = '\0';
  lpText2[len2] = '\0';
  int nRet = ((SQLite3Collation*) collation)->Compare(lpText1, lpText2);
  free(lpText1);
  free(lpText2);
  return nRet;
}

void SQLite3Database::ExecCollationNeeded(void* db, void*, int, const char* collationName)
{
  ((SQLite3Database*) db)->SetNeededCollation(collationName);
}

void SQLite3Database::GetMetaData(const StringEx& databaseName, const StringEx& tableName, const StringEx& columnName,
                                    StringEx* dataType, StringEx* collation, bool* notNull, bool* primaryKey, bool* autoIncrement)
{
#if SQLITE3EX3_HAVE_METADATA
  const char* localDatabaseName = ConvertToUTF8(databaseName);
  if (databaseName == "") localDatabaseName = "";
  const char* localTableName = ConvertToUTF8(tableName);
  const char* localColumnName = ConvertToUTF8(columnName);
  const char* localDataType;
  const char* localCollation;
  int localNotNull;
  int localPrimaryKey;
  int localAutoIncrement;
  int rc = sqlite3_table_column_metadata((sqlite3*) m_db, localDatabaseName, localTableName, localColumnName,
                                         &localDataType, &localCollation, &localNotNull, &localPrimaryKey, &localAutoIncrement);

  if (rc != SQLITE_OK)
  {
    const char* localError = sqlite3_errmsg((sqlite3*) m_db);
    throw SQLite3Exception(rc, localError);
  }

  if (dataType      != NULL) *dataType      = ConvertToUTF8(localDataType);
  if (collation     != NULL) *collation     = ConvertToUTF8(localCollation);

  if (notNull       != NULL) *notNull       = (localNotNull       != 0);
  if (primaryKey    != NULL) *primaryKey    = (localPrimaryKey    != 0);
  if (autoIncrement != NULL) *autoIncrement = (localAutoIncrement != 0);
#else
  throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_NOMETADATA);
#endif
}

void SQLite3Database::LoadExtension(const StringEx& fileName, const StringEx& entryPoint)
{
#if SQLITE3EX3_HAVE_LOAD_EXTENSION
  int rc = sqlite3_load_extension((sqlite3 *) m_db, fileName.c_str(), entryPoint.c_str(), NULL);
  if (rc != SQLITE_OK)
  {
    const char* localError = sqlite3_errmsg((sqlite3*) m_db);
    throw SQLite3Exception(rc, localError);
  }
#else
  throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_NOLOADEXT);
#endif
}

void SQLite3Database::EnableLoadExtension(bool enable)
{
#if SQLITE3EX3_HAVE_LOAD_EXTENSION
  int onoff = (enable) ? 1 : 0;
  int rc = sqlite3_enable_load_extension((sqlite3 *) m_db, onoff);
  if (rc != SQLITE_OK)
  {
    const char* localError = sqlite3_errmsg((sqlite3*) m_db);
    throw SQLite3Exception(rc, localError);
  }
#else
  throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_NOLOADEXT);
#endif
}

void SQLite3Database::ReKey(const StringEx& newKey)
{
#ifdef SQLITE_HAS_CODEC
  MemoryBuffer binaryNewKey;
  if (newKey.length() > 0)
  {
    binaryNewKey.AppendData((void*)(newKey.c_str()), newKey.length());
  }
  ReKey(binaryNewKey);
#else
  throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_NOCODEC);
#endif
}

void SQLite3Database::ReKey(const MemoryBuffer& newKey)
{
#ifdef SQLITE_HAS_CODEC
  int rc = sqlite3_rekey((sqlite3*) m_db, newKey.GetData(), (int) newKey.GetDataLen());
  if (rc != SQLITE_OK)
  {
    const char* localError = sqlite3_errmsg((sqlite3*) m_db);
    throw SQLite3Exception(rc, localError);
  }
#else
  throw SQLite3Exception(SQLITE3EX_ERROR, ERRMSG_NOCODEC);
#endif
}

int SQLite3Database::GetLimit(SQLite3LimitType id)
{
  int value = -1;
#if SQLITE_VERSION_NUMBER >= 3005008
  CheckDatabase();
  if (id >= SQLITE3EX_LIMIT_LENGTH && id <= SQLITE3EX_LIMIT_VARIABLE_NUMBER)
  {
    value = sqlite3_limit((sqlite3 *) m_db, id, -1);
  }
#else
#endif
  return value;
}

int SQLite3Database::SetLimit(SQLite3LimitType id, int newValue)
{
  int value = -1;
#if SQLITE_VERSION_NUMBER >= 3005008
  CheckDatabase();
  if (id >= SQLITE3EX_LIMIT_LENGTH && id <= SQLITE3EX_LIMIT_VARIABLE_NUMBER)
  {
    value = sqlite3_limit((sqlite3 *) m_db, id, newValue);
  }
#else
#endif
  return value;
}

static const CharEx* limitCodeString[] =
{ _T("SQLITE_LIMIT_LENGTH"),              _T("SQLITE_LIMIT_SQL_LENGTH"),
  _T("SQLITE_LIMIT_COLUMN"),              _T("SQLITE_LIMIT_EXPR_DEPTH"),
  _T("SQLITE_LIMIT_COMPOUND_SELECT"),     _T("SQLITE_LIMIT_VDBE_OP"),
  _T("SQLITE_LIMIT_FUNCTION_ARG"),        _T("SQLITE_LIMIT_ATTACHED"),
  _T("SQLITE_LIMIT_LIKE_PATTERN_LENGTH"), _T("SQLITE_LIMIT_VARIABLE_NUMBER"),
  _T("SQLITE_LIMIT_TRIGGER_DEPTH")
};


/* static */
StringEx SQLite3Database::LimitTypeToString(SQLite3LimitType type)
{
  const CharEx* limitString = _T("Unknown");
  if (type >= SQLITE3EX_LIMIT_LENGTH && type <= SQLITE3EX_LIMIT_VARIABLE_NUMBER)
  {
    limitString = limitCodeString[type];
  }
  return StringEx(limitString);
}

/* static */
void SQLite3Database::InitializeSQLite()
{
#if SQLITE_VERSION_NUMBER >= 3006000
  int rc = sqlite3_initialize();
  if (rc != SQLITE_OK)
  {
    throw SQLite3Exception(rc, ERRMSG_INITIALIZE);
  }
#endif
}

/* static */
void SQLite3Database::ShutdownSQLite()
{
#if SQLITE_VERSION_NUMBER >= 3006000
  int rc = sqlite3_shutdown();
  if (rc != SQLITE_OK)
  {
    throw SQLite3Exception(rc, ERRMSG_SHUTDOWN);
  }
#endif
}

/* static */
bool SQLite3Database::Randomness(int n, MemoryBuffer& random)
{
  bool ok = false;
#if SQLITE_VERSION_NUMBER >= 3005008
  if (n > 0)
  {
    void* buffer = random.GetWriteBuf(n);
    sqlite3_randomness(n, buffer);
    random.UngetWriteBuf(n);
    ok = true;
  }
#else
#endif
  return ok;
}

// ----------------------------------------------------------------------------
// SQLite3FunctionContext: class providing the function context
//                           for user defined functions
// ----------------------------------------------------------------------------

int SQLite3FunctionContext::GetArgCount()
{
  return m_argc;
}

int SQLite3FunctionContext::GetArgType(int argIndex)
{
  if (argIndex >= 0 && argIndex < m_argc)
  {
    return sqlite3_value_type((sqlite3_value*) m_argv[argIndex]);
  }
  else
  {
    return SQLITE_NULL;
  }
}

bool SQLite3FunctionContext::IsNull(int argIndex)
{
  if (argIndex >= 0 && argIndex < m_argc)
  {
    return sqlite3_value_type((sqlite3_value*) m_argv[argIndex]) == SQLITE_NULL;
  }
  else
  {
    return true;
  }
}

int SQLite3FunctionContext::GetInt(int argIndex, int nullValue)
{
  if (argIndex >= 0 && argIndex < m_argc)
  {
    if (!IsNull(argIndex))
    {
      return sqlite3_value_int((sqlite3_value*) m_argv[argIndex]);
    }
    else
    {
      return nullValue;
    }
  }
  else
  {
    return nullValue;
  }
}

Int64Ex SQLite3FunctionContext::GetInt64(int argIndex, Int64Ex nullValue)
{
  if (argIndex >= 0 && argIndex < m_argc)
  {
    if (!IsNull(argIndex))
    {
      return Int64Ex(sqlite3_value_int64((sqlite3_value*) m_argv[argIndex]));
    }
    else
    {
      return nullValue;
    }
  }
  else
  {
    return nullValue;
  }
}

double SQLite3FunctionContext::GetDouble(int argIndex, double nullValue)
{
  if (argIndex >= 0 && argIndex < m_argc)
  {
    if (!IsNull(argIndex))
    {
      return sqlite3_value_double((sqlite3_value*) m_argv[argIndex]);
    }
    else
    {
      return nullValue;
    }
  }
  else
  {
    return nullValue;
  }
}

StringEx SQLite3FunctionContext::GetString(int argIndex, const StringEx& nullValue)
{
  if (argIndex >= 0 && argIndex < m_argc)
  {
    if (!IsNull(argIndex))
    {
      const char* localValue = (const char*) sqlite3_value_text((sqlite3_value*) m_argv[argIndex]);
      return localValue;
    }
    else
    {
      return nullValue;
    }
  }
  else
  {
    return nullValue;
  }
}

MemoryBuffer& SQLite3FunctionContext::GetBlob(int argIndex, MemoryBuffer& buffer)
{
  if (argIndex >= 0 && argIndex < m_argc)
  {
    if (!IsNull(argIndex))
    {
      int len = sqlite3_value_bytes((sqlite3_value*) m_argv[argIndex]);
      const void* blob = sqlite3_value_blob((sqlite3_value*) m_argv[argIndex]);
      buffer.AppendData((void*) blob, (size_t) len);
    }
  }
  return buffer;
}

void SQLite3FunctionContext::SetResult(int value)
{
  sqlite3_result_int((sqlite3_context*) m_ctx, value);
}

void SQLite3FunctionContext::SetResult(Int64Ex value)
{
  sqlite3_result_int64((sqlite3_context*) m_ctx, value);
}

void SQLite3FunctionContext::SetResult(double value)
{
  sqlite3_result_double((sqlite3_context*) m_ctx, value);
}

void SQLite3FunctionContext::SetResult(const StringEx& value)
{
  sqlite3_result_text((sqlite3_context*) m_ctx, value.c_str(), -1, SQLITE_TRANSIENT);
}

void SQLite3FunctionContext::SetResult(unsigned char* value, int len)
{
  sqlite3_result_blob((sqlite3_context*) m_ctx, value, len, SQLITE_TRANSIENT);
}

void SQLite3FunctionContext::SetResult(const MemoryBuffer& buffer)
{
  sqlite3_result_blob((sqlite3_context*) m_ctx, buffer.GetData(), (int) buffer.GetDataLen(), SQLITE_TRANSIENT);
}

void SQLite3FunctionContext::SetResultNull()
{
  sqlite3_result_null((sqlite3_context*) m_ctx);
}

void SQLite3FunctionContext::SetResultZeroBlob(int blobSize)
{
#if SQLITE_VERSION_NUMBER >= 3004000
  sqlite3_result_zeroblob((sqlite3_context*) m_ctx, blobSize);
#endif
}

void SQLite3FunctionContext::SetResultArg(int argIndex)
{
  if (argIndex >= 0 && argIndex < m_argc) {
    sqlite3_result_value((sqlite3_context*) m_ctx, (sqlite3_value*) m_argv[argIndex]);
  } else {
    sqlite3_result_null((sqlite3_context*) m_ctx);
  }
}

void SQLite3FunctionContext::SetResultError(const StringEx& errmsg)
{
  sqlite3_result_error((sqlite3_context*) m_ctx, errmsg.c_str(), -1);
}

int SQLite3FunctionContext::GetAggregateCount()
{
  if (m_isAggregate)
  {
    return m_count;
  }
  else
  {
    return 0;
  }
}

void* SQLite3FunctionContext::GetAggregateStruct(int len)
{
  if (m_isAggregate)
  {
    return sqlite3_aggregate_context((sqlite3_context*) m_ctx, len);
  }
  else
  {
    return NULL;
  }
}

/* static */
void SQLite3FunctionContext::ExecScalarFunction(void* ctx, int argc, void** argv)
{
  SQLite3FunctionContext context(ctx, false, argc, argv);
  SQLite3ScalarFunction* func = (SQLite3ScalarFunction*) sqlite3_user_data((sqlite3_context*) ctx);
  func->Execute(context);
}

/* static */
void SQLite3FunctionContext::ExecAggregateStep(void* ctx, int argc, void** argv)
{
  SQLite3FunctionContext context(ctx, true, argc, argv);
  SQLite3AggregateFunction* func = (SQLite3AggregateFunction*) sqlite3_user_data((sqlite3_context*) ctx);
  func->m_count++;
  context.m_count = func->m_count;
  func->Aggregate(context);
}

/* static */
void SQLite3FunctionContext::ExecAggregateFinalize(void* ctx)
{
  SQLite3FunctionContext context(ctx, true, 0, NULL);
  SQLite3AggregateFunction* func = (SQLite3AggregateFunction*) sqlite3_user_data((sqlite3_context*) ctx);
  context.m_count = func->m_count;
  func->Finalize(context);
}

/* static */
int SQLite3FunctionContext::ExecAuthorizer(void* func, int type,
                                             const char* arg1, const char* arg2,
                                             const char* arg3, const char* arg4)
{
  SQLite3Authorizer::AuthorizationCode localType = (SQLite3Authorizer::AuthorizationCode) type;
  return (int) ((SQLite3Authorizer*) func)->Authorize(localType, arg1, arg2, arg3, arg4);
}

/* static */
int SQLite3FunctionContext::ExecCommitHook(void* hook)
{
  return (int) ((SQLite3Hook*) hook)->CommitCallback();
}

/* static */
void SQLite3FunctionContext::ExecRollbackHook(void* hook)
{
  ((SQLite3Hook*) hook)->RollbackCallback();
}

/* static */
void SQLite3FunctionContext::ExecUpdateHook(void* hook, int type,
                                              const char* database, const char* table,
                                              Int64Ex rowid)
{
  SQLite3Hook::UpdateType locType = (SQLite3Hook::UpdateType) type;
  Int64Ex locRowid = rowid;
  ((SQLite3Hook*) hook)->UpdateCallback(locType, database, table, locRowid);
}

SQLite3FunctionContext::SQLite3FunctionContext(void* ctx, bool isAggregate, int argc, void** argv)
: m_ctx(ctx), m_isAggregate(isAggregate), m_count(0), m_argc(argc), m_argv(argv)
{
}

/* static */
int SQLite3FunctionContext::ExecWriteAheadLogHook(void* hook, void* dbHandle,
                                                    const char* database, int numPages)
{
  return (int) ((SQLite3Hook*) hook)->WriteAheadLogCallback(database, numPages);
}

static const CharEx* authCodeString[] =
{ _T("SQLITE_COPY"),              _T("SQLITE_CREATE_INDEX"),      _T("SQLITE_CREATE_TABLE"),
  _T("SQLITE_CREATE_TEMP_INDEX"), _T("SQLITE_CREATE_TEMP_TABLE"), _T("SQLITE_CREATE_TEMP_TRIGGER"),
  _T("SQLITE_CREATE_TEMP_VIEW"),  _T("SQLITE_CREATE_TRIGGER"),    _T("SQLITE_CREATE_VIEW"),
  _T("SQLITE_DELETE"),            _T("SQLITE_DROP_INDEX"),        _T("SQLITE_DROP_TABLE"),
  _T("SQLITE_DROP_TEMP_INDEX"),   _T("SQLITE_DROP_TEMP_TABLE"),   _T("SQLITE_DROP_TEMP_TRIGGER"),
  _T("SQLITE_DROP_TEMP_VIEW"),    _T("SQLITE_DROP_TRIGGER"),      _T("SQLITE_DROP_VIEW"),
  _T("SQLITE_INSERT"),            _T("SQLITE_PRAGMA"),            _T("SQLITE_READ"),
  _T("SQLITE_SELECT"),            _T("SQLITE_TRANSACTION"),       _T("SQLITE_UPDATE"),
  _T("SQLITE_ATTACH"),            _T("SQLITE_DETACH"),            _T("SQLITE_ALTER_TABLE"),
  _T("SQLITE_REINDEX"),           _T("SQLITE_ANALYZE"),           _T("SQLITE_CREATE_VTABLE"),
  _T("SQLITE_DROP_VTABLE"),       _T("SQLITE_FUNCTION"),          _T("SQLITE_SAVEPOINT")
};


/* static */
StringEx SQLite3Authorizer::AuthorizationCodeToString(SQLite3Authorizer::AuthorizationCode type)
{
  const CharEx* authString = _T("Unknown");
  if (type >= SQLITE_COPY && type <= SQLITE_MAX_CODE)
  {
    authString = authCodeString[type];
  }
  return StringEx(authString);
}

// ----------------------------------------------------------------------------
// SQLite3Transaction
// ----------------------------------------------------------------------------

SQLite3Transaction::SQLite3Transaction(SQLite3Database* db, SQLite3TransactionType transactionType)
{
  assert(db != NULL);
  m_database = db;
  try
  {
    m_database->Begin(transactionType);
  }
  catch (...)
  {
    m_database = NULL; // Flag that transaction is not active
  }
}

SQLite3Transaction::~SQLite3Transaction()
{
  if (m_database != NULL)
  {
    m_database->Rollback();
  }
}

void SQLite3Transaction::Commit()
{
  try
  {
    m_database->Commit();
  }
  catch (...)
  {
    m_database->Rollback();
  }
  m_database = NULL;
}

void SQLite3Transaction::Rollback()
{
  try
  {
    m_database->Rollback();
  }
  catch (...)
  {
    m_database->Rollback();
  }
  m_database = NULL;
}

// --- Support for named collections

#if SQLITE3EX3_USE_NAMED_COLLECTIONS

// The following code is based on the SQLite test_intarray source code.

/// Definition of the sqlite3_intarray object (internal)
struct sqlite3_intarray
{
  int n;                    // Number of elements in the array
  sqlite3_int64* a;         // Contents of the array
  void (*xFree)(void*);     // Function used to free a[]
};

// Objects used internally by the virtual table implementation
typedef struct intarray_vtab intarray_vtab;
typedef struct intarray_cursor intarray_cursor;

/// Definition of intarray table object (internal)
struct intarray_vtab
{
  sqlite3_vtab base;            // Base class
  sqlite3_intarray* pContent;   // Content of the integer array
};

/// Definition of  intarray cursor object (internal)
struct intarray_cursor
{
  sqlite3_vtab_cursor base;    // Base class
  int i;                       // Current cursor position
};

// Free an sqlite3_intarray object.
static void intarrayFree(sqlite3_intarray* p)
{
  if (p->a != NULL && p->xFree)
  {
    p->xFree(p->a);
  }
  sqlite3_free(p);
}

// Table destructor for the intarray module.
static int intarrayDestroy(sqlite3_vtab* p)
{
  intarray_vtab* pVtab = (intarray_vtab*)p;
  sqlite3_free(pVtab);
  return 0;
}

// Table constructor for the intarray module.
static int intarrayCreate(sqlite3* db,                  // Database where module is created
                          void* pAux,                   // clientdata for the module
                          int /*argc*/,                 // Number of arguments
                          const char* const* /*argv*/,  // Value for all arguments
                          sqlite3_vtab** ppVtab,        // Write the new virtual table object here
                          char** /*pzErr*/)             // Put error message text here
{
  int rc = SQLITE_NOMEM;
  intarray_vtab* pVtab = (intarray_vtab*) sqlite3_malloc(sizeof(intarray_vtab));

  if (pVtab)
  {
    memset(pVtab, 0, sizeof(intarray_vtab));
    pVtab->pContent = (sqlite3_intarray*)pAux;
    rc = sqlite3_declare_vtab(db, "CREATE TABLE x(value INTEGER PRIMARY KEY)");
  }
  *ppVtab = (sqlite3_vtab*)pVtab;
  return rc;
}

// Open a new cursor on the intarray table.
static int intarrayOpen(sqlite3_vtab* /*pVTab*/, sqlite3_vtab_cursor** ppCursor)
{
  int rc = SQLITE_NOMEM;
  intarray_cursor* pCur = (intarray_cursor*) sqlite3_malloc(sizeof(intarray_cursor));
  if (pCur)
  {
    memset(pCur, 0, sizeof(intarray_cursor));
    *ppCursor = (sqlite3_vtab_cursor *)pCur;
    rc = SQLITE_OK;
  }
  return rc;
}

// Close a intarray table cursor.
static int intarrayClose(sqlite3_vtab_cursor* cur)
{
  intarray_cursor* pCur = (intarray_cursor*)cur;
  sqlite3_free(pCur);
  return SQLITE_OK;
}

// Retrieve a column of data.
static int intarrayColumn(sqlite3_vtab_cursor* cur, sqlite3_context* ctx, int /*i*/)
{
  intarray_cursor* pCur = (intarray_cursor*)cur;
  intarray_vtab* pVtab = (intarray_vtab*)cur->pVtab;
  if (pCur->i >= 0 && pCur->i < pVtab->pContent->n)
  {
    sqlite3_result_int64(ctx, pVtab->pContent->a[pCur->i]);
  }
  return SQLITE_OK;
}

// Retrieve the current rowid.
static int intarrayRowid(sqlite3_vtab_cursor* cur, sqlite_int64* pRowid)
{
  intarray_cursor* pCur = (intarray_cursor*)cur;
  *pRowid = pCur->i;
  return SQLITE_OK;
}

static int intarrayEof(sqlite3_vtab_cursor* cur)
{
  intarray_cursor* pCur = (intarray_cursor*)cur;
  intarray_vtab* pVtab = (intarray_vtab*)cur->pVtab;
  return pCur->i >= pVtab->pContent->n;
}

// Advance the cursor to the next row.
static int intarrayNext(sqlite3_vtab_cursor* cur)
{
  intarray_cursor* pCur = (intarray_cursor*)cur;
  pCur->i++;
  return SQLITE_OK;
}

// Reset a intarray table cursor.
static int intarrayFilter(sqlite3_vtab_cursor* pVtabCursor,
                          int /*idxNum*/, const char* /*idxStr*/,
                          int /*argc*/, sqlite3_value** /*argv*/)
{
  intarray_cursor *pCur = (intarray_cursor *)pVtabCursor;
  pCur->i = 0;
  return SQLITE_OK;
}

// Analyse the WHERE condition.
static int intarrayBestIndex(sqlite3_vtab* /*tab*/, sqlite3_index_info* /*pIdxInfo*/)
{
  return SQLITE_OK;
}

// Definition of a virtual table module for integer collections
static sqlite3_module intarrayModule =
{
  0,                           // iVersion
  intarrayCreate,              // xCreate - create a new virtual table
  intarrayCreate,              // xConnect - connect to an existing vtab
  intarrayBestIndex,           // xBestIndex - find the best query index
  intarrayDestroy,             // xDisconnect - disconnect a vtab
  intarrayDestroy,             // xDestroy - destroy a vtab
  intarrayOpen,                // xOpen - open a cursor
  intarrayClose,               // xClose - close a cursor
  intarrayFilter,              // xFilter - configure scan constraints
  intarrayNext,                // xNext - advance a cursor
  intarrayEof,                 // xEof
  intarrayColumn,              // xColumn - read data
  intarrayRowid,               // xRowid - read data
  0,                           // xUpdate
  0,                           // xBegin
  0,                           // xSync
  0,                           // xCommit
  0,                           // xRollback
  0,                           // xFindMethod
  0,                           // xRename
};

/// Definition of the sqlite3_chararray object (internal)
struct sqlite3_chararray
{
  int n;                    // Number of elements in the array
  char** a;                 // Contents of the array
  void (*xFree)(void*);     // Function used to free a[]
};

// Objects used internally by the virtual table implementation
typedef struct chararray_vtab chararray_vtab;
typedef struct chararray_cursor chararray_cursor;

/// Definition of chararray table object (internal)
struct chararray_vtab
{
  sqlite3_vtab base;            // Base class
  sqlite3_chararray* pContent;  // Content of the char array
};

/// Definition of chararray cursor object (internal)
struct chararray_cursor
{
  sqlite3_vtab_cursor base;    // Base class
  int i;                       // Current cursor position
};

// Free an sqlite3_chararray object.
static void chararrayFree(sqlite3_chararray* p)
{
  if (p->a != NULL && p->xFree)
  {
    int j;
    for (j = 0; j < p->n; ++j)
    {
      p->xFree(p->a[j]);
    }
    p->xFree(p->a);
  }
  sqlite3_free(p);
}

// Table destructor for the chararray module.
static int chararrayDestroy(sqlite3_vtab* p)
{
  chararray_vtab* pVtab = (chararray_vtab*)p;
  sqlite3_free(pVtab);
  return 0;
}

// Table constructor for the chararray module.
static int chararrayCreate(sqlite3* db,                  // Database where module is created
                           void* pAux,                   // clientdata for the module
                           int /*argc*/,                 // Number of arguments
                           const char* const* /*argv*/,  // Value for all arguments
                           sqlite3_vtab** ppVtab,        // Write the new virtual table object here
                           char** /*pzErr*/)             // Put error message text here
{
  int rc = SQLITE_NOMEM;
  chararray_vtab* pVtab = (chararray_vtab*) sqlite3_malloc(sizeof(chararray_vtab));

  if (pVtab)
  {
    memset(pVtab, 0, sizeof(chararray_vtab));
    pVtab->pContent = (sqlite3_chararray*)pAux;
    rc = sqlite3_declare_vtab(db, "CREATE TABLE x(value CHAR PRIMARY KEY)");
  }
  *ppVtab = (sqlite3_vtab*)pVtab;
  return rc;
}

// Open a new cursor on the chararray table.
static int chararrayOpen(sqlite3_vtab* /*pVTab*/, sqlite3_vtab_cursor** ppCursor)
{
  int rc = SQLITE_NOMEM;
  chararray_cursor* pCur = (chararray_cursor*) sqlite3_malloc(sizeof(chararray_cursor));
  if (pCur)
  {
    memset(pCur, 0, sizeof(chararray_cursor));
    *ppCursor = (sqlite3_vtab_cursor *)pCur;
    rc = SQLITE_OK;
  }
  return rc;
}

// Close a chararray table cursor.
static int chararrayClose(sqlite3_vtab_cursor* cur)
{
  chararray_cursor* pCur = (chararray_cursor*)cur;
  sqlite3_free(pCur);
  return SQLITE_OK;
}

// Retrieve a column of data.
static int chararrayColumn(sqlite3_vtab_cursor* cur, sqlite3_context* ctx, int /*i*/)
{
  chararray_cursor* pCur = (chararray_cursor*)cur;
  chararray_vtab* pVtab = (chararray_vtab*)cur->pVtab;
  if (pCur->i >= 0 && pCur->i < pVtab->pContent->n)
  {
    sqlite3_result_text(ctx, pVtab->pContent->a[pCur->i], -1, SQLITE_STATIC);
  }
  return SQLITE_OK;
}

// Retrieve the current rowid.
static int chararrayRowid(sqlite3_vtab_cursor* cur, sqlite_int64* pRowid)
{
  chararray_cursor* pCur = (chararray_cursor*)cur;
  *pRowid = pCur->i;
  return SQLITE_OK;
}

static int chararrayEof(sqlite3_vtab_cursor* cur)
{
  chararray_cursor* pCur = (chararray_cursor*)cur;
  chararray_vtab* pVtab = (chararray_vtab*)cur->pVtab;
  return pCur->i >= pVtab->pContent->n;
}

// Advance the cursor to the next row.
static int chararrayNext(sqlite3_vtab_cursor* cur)
{
  chararray_cursor* pCur = (chararray_cursor*)cur;
  pCur->i++;
  return SQLITE_OK;
}

// Reset a chararray table cursor.
static int chararrayFilter(sqlite3_vtab_cursor* pVtabCursor,
                           int /*idxNum*/, const char* /*idxStr*/,
                           int /*argc*/, sqlite3_value** /*argv*/)
{
  chararray_cursor *pCur = (chararray_cursor *)pVtabCursor;
  pCur->i = 0;
  return SQLITE_OK;
}

// Analyse the WHERE condition.
static int chararrayBestIndex(sqlite3_vtab* /*tab*/, sqlite3_index_info* /*pIdxInfo*/)
{
  return SQLITE_OK;
}

// Definition of a virtual table module for StringEx collections
static sqlite3_module chararrayModule =
{
  0,                           // iVersion
  chararrayCreate,             // xCreate - create a new virtual table
  chararrayCreate,             // xConnect - connect to an existing vtab
  chararrayBestIndex,          // xBestIndex - find the best query index
  chararrayDestroy,            // xDisconnect - disconnect a vtab
  chararrayDestroy,            // xDestroy - destroy a vtab
  chararrayOpen,               // xOpen - open a cursor
  chararrayClose,              // xClose - close a cursor
  chararrayFilter,             // xFilter - configure scan constraints
  chararrayNext,               // xNext - advance a cursor
  chararrayEof,                // xEof
  chararrayColumn,             // xColumn - read data
  chararrayRowid,              // xRowid - read data
  0,                           // xUpdate
  0,                           // xBegin
  0,                           // xSync
  0,                           // xCommit
  0,                           // xRollback
  0,                           // xFindMethod
  0,                           // xRename
};

#endif // SQLITE3EX3_USE_NAMED_COLLECTIONS
